1. 事務
1.1. 為什么需要事務
現在的很多軟件都是多用戶,多程序,多線程的,對同一個表可能同時有很多人在用,為保持數據的一致性,所以提出了事務的概念。
A 給B 要划錢,A 的賬戶-1000元, B 的賬戶就要+1000元,這兩個update 語句必須作為一個整體來執行,不然A 扣錢了,B 沒有加錢這種情況很難處理。
1.2. 什么存儲引擎支持事務
1.查看數據庫下面是否支持事務(InnoDB支持)?
show engines;
2.查看mysql當前默認的存儲引擎?
show variables like '%storage_engine%';
3.查看某張表的存儲引擎?
show create table 表名 ;
4.對於表的存儲結構的修改?
建立InnoDB 表:Create table .... type=InnoDB; Alter table table_name type=InnoDB;
1.3. 事務特性
事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性。
l 原子性(atomicity)
l 一致性(consistency)
l 隔離性(isolation)
l 持久性(durability)
1.3.1. 原子性(atomicity)
一個事務必須被視為一個不可分割的最小單元,整個事務中的所有操作要么全部提交成功,要么全部失敗,對於一個事務來說,不可能只執行其中的一部分操作
老婆大人給Deer發生活費
1.老婆大人工資卡扣除500元
2.Deer工資卡增加500
整個事務要么全部成功,要么全部失敗
1.3.2. 一致性(consistency)
一致性是指事務將數據庫從一種一致性轉換到另外一種一致性狀態,在事務開始之前和事務結束之后數據庫中數據的完整性沒有被破壞
老婆大人給Deer發生活費
1.老婆大人工資卡扣除500元
2.Deer工資卡增加500
2.Deer工資卡增加1000
扣除的錢(-500) 與增加的錢(500) 相加應該為0
1.3.3. 持久性(durability)
一旦事務提交,則其所做的修改就會永久保存到數據庫中。此時即使系統崩潰,已經提交的修改數據也不會丟失
並不是數據庫的角度完全能解決
1.3.4. 隔離性(isolation)
一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對並發的其他事務是隔離的,並發執行的各個事務之間不能互相干擾。
(對數據庫的並行執行,應該像串行執行一樣)
l 未提交讀(READ UNCOMMITED)臟讀
l 已提交讀 (READ COMMITED)不可重復讀
l 可重復讀(REPEATABLE READ)
l 可串行化(SERIALIZABLE)
mysql默認的事務隔離級別為repeatable-read
show variables like '%tx_isolation%';
1.3.4.1. 事務並發問題
l 臟讀:事務A讀取了事務B更新的數據,然后B回滾操作,那么A讀取到的數據是臟數據
l 不可重復讀:事務 A 多次讀取同一數據,事務 B 在事務A多次讀取的過程中,對數據作了更新並提交,導致事務A多次讀取同一數據時,結果 不一致。
l 幻讀:系統管理員A將數據庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員B就在這個時候插入了一條具體分數的記錄,當系統管理員A改結束后發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。
不可重復讀的和幻讀很容易混淆,不可重復讀側重於修改,幻讀側重於新增或刪除。解決不可重復讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表
1.3.4.2. 未提交讀(READ UNCOMMITED)臟讀
show variables like '%tx_isolation%';
set SESSION TRANSACTION ISOLATION LEVEL read UNCOMMITTED;
一個session中
start TRANSACTION
update account set balance = balance -50 where id = 1
另外一個session中查詢
select * from account
回到第一個session中 回滾事務
ROLLBACK
在第二個session中
select * from account
在另外一個session中讀取到了為提交的數據,這部分的數據為臟數據
1.3.4.3. 已提交讀 (READ COMMITED)不可重復讀
show variables like '%tx_isolation%';
set SESSION TRANSACTION ISOLATION LEVEL read committed;
一個session中
start TRANSACTION
update account set balance = balance -50 where id = 1
另外一個session中查詢 (數據並沒改變)
select * from account
回到第一個session中 回滾事務
commit
在第二個session中
select * from account (數據已經改變)
1.3.4.4. 可重復讀(REPEATABLE READ)
show variables like '%tx_isolation%';
set SESSION TRANSACTION ISOLATION LEVEL repeatable read;
一個session中
start TRANSACTION
update account set balance = balance -50 where id = 1
另外一個session中查詢 (數據並沒改變)
select * from account
回到第一個session中 回滾事務
commit
在第二個session中
select * from account (數據並未改變)
1.3.4.5. 可串行化(SERIALIZABLE)
show variables like '%tx_isolation%';
set SESSION TRANSACTION ISOLATION LEVEL serializable;
1.開啟一個事務
begin
select * from account 發現3條記錄
2.開啟另外一個事務
begin
select * from account 發現3條記錄 也是3條記錄
insert into account VALUES(4,'deer',500) 發現根本就不讓插入
3.回到第一個事務 commit
1.1.1.1. 間隙鎖(gap鎖)
其實在mysql中,可重復讀已經解決了幻讀問題,借助的就是間隙鎖
實驗1:
select @@tx_isolation;
create table t_lock_1 (a int primary key);
insert into t_lock_1 values(10),(11),(13),(20),(40);
begin
select * from t_lock_1 where a <= 13 for update;
在另外一個會話中
insert into t_lock_1 values(21) 成功
insert into t_lock_1 values(19) 阻塞
在rr隔離級別中者個會掃描到當前值(13)的下一個值(20),並把這些數據全部加鎖
實驗:2
create table t_lock_2 (a int primary key,b int, key (b));
insert into t_lock_2 values(1,1),(3,1),(5,3),(8,6),(10,8);
會話1
BEGIN
select * from t_lock_2 where b=3 for update;
1 3 5 8 10
1 1 3 6 8
會話2
select * from t_lock_2 where a = 5 lock in share mode; -- 不可執行,因為a=5上有一把記錄鎖
insert into t_lock_2 values(4, 2); -- 不可以執行,因為b=2在(1, 3]內
insert into t_lock_2 values(6, 5); -- 不可以執行,因為b=5在(3, 6)內
insert into t_lock_2 values(2, 0); -- 可以執行,(2, 0)均不在鎖住的范圍內
insert into t_lock_2 values(6, 7); -- 可以執行,(6, 7)均不在鎖住的范圍內
insert into t_lock_2 values(9, 6); -- 可以執行
insert into t_lock_2 values(7, 6); -- 不可以執行
二級索引鎖住的范圍是 (1, 3],(3, 6)
主鍵索引只鎖住了a=5的這條記錄 [5]
1.1. 事務語法
1.1.1. 開啟事務
1、begin
2、START TRANSACTION(推薦)
3、begin work
1.1.2. 事務回滾
rollback
1.1.3. 事務提交
commit
1.1.4. 還原點
savepoint
show variables like '%autocommit%'; 自動提交事務是開啟的
set autocommit=0; //關閉事務自動提交
insert into testdemo values(5,5,5);
savepoint s1;
insert into testdemo values(6,6,6);
savepoint s2;
insert into testdemo values(7,7,7);
savepoint s3;
select * from testdemo
rollback to savepoint s2
rollback