Mysql 中的MVCC原理,undo日誌的依賴
?
原文點擊:MVCC原理淺析
- MVCC: Multi-Version Concurrency Control
多版本並發控制:當mysql 開啟事務操作時,或者數據庫崩潰恢復,都會用到MVCC機制,而不只是單單靠行鎖去實現。而是一起使用
?
當使用鎖(尤其排他鎖-行鎖)進行並發控制,開銷是非常大的 ,而使用MVCC機制來做,能一定程度的代替行鎖,就可以有效降低系統開銷
InnoDB的MVCC,是通過在每行記錄後面保存兩個隱藏的列來實現的,這兩個列,分別保存了這個行的創建時間,一個保存的是行的刪除時間。這裏存儲的並不是實際的時間值,而是系統版本號(可以理解為事務的ID),沒開始一個新的事務,系統版本號就會自動遞增,事務開始時刻的系統版本號會作為事務的ID.
話外內容:############開始###############
InnoDB的內核,會對所有row數據增加三個內部屬性:
(1)DB_TRX_ID,6字節,記錄每一行最近一次修改它的事務ID;
(2)DB_ROLL_PTR,7字節,記錄指向回滾段undo日誌的指針;
(3)DB_ROW_ID,6字節,單調遞增的行ID;
話外內容:############結束###############
如:列子,假設版本好初始為1
create table tb(
id int auto_increment primary key,
name cahr(32));
創建幾條數據:insert 操作:第一個事務ID為1
start transaction;
insert into tb(name) values('shenjian') ;
insert into tb(name) values('zhangshan');
insert into tb(name) values('lisi');
commit;
id | name | 創建時間(事務ID) | 刪除時間(事務ID) |
---|---|---|---|
1 | shenjian | 1 | undefinded |
2 | zhangsan | 1 | undefinded |
3 | lisi | 1 | undefinded |
對select 的影響:
innodb 只會去查找版本好小於或等於(早於)當前事務ID的數據行,這樣可以確保事務讀取的行,要麽是在事務開始前已經存在的,要麽是事務自身插入或者修改過的
行的刪除版本要麽未定義,要麽大於當前事務版本號,這可以確保事務讀取到的行,在事務開始之前未被刪除.
只有1、2同時滿足的記錄,才能返回作為查詢結果.
例2:delete操作
InnoDB會為刪除的每一行保存當前系統的版本號(事務的ID)作為刪除標識.
看下面的具體例子分析:
第二個事務,ID為2;
start transaction;
select * from tb; //(第一步操作:1)
select * from tb; //(第二部操作:2)
commit;
假設:在這個事務的的執行過程中到第一步操作:1 的時候。
又開啟了一個新事務(ID 為3)向表中執行Insert 操作插入數據
start transaction;
insert into tb(name) values('wangwu') ;
commit;
此時的表中數據為:
id | name | 創建時間(事務ID) | 刪除時間(事務ID) |
---|---|---|---|
1 | shenjian | 1 | undefinded |
2 | zhangsan | 1 | undefinded |
3 | lisi | 1 | undefinded |
4 | wangwu | 3 | undefinded |
然後剛才事務2,中的第二步:2 開始執行,引文id=4的數據的創建時間(事務ID為3),執行當前事務的ID為2,而InnoDB只會查找事務ID小於等於當前事務ID的數據行,所以id=4的數據行並不會在執行事務2中的第二步:2 中被檢索出來,在事務2中的兩條select 語句檢索出來的數據都只會下表:
id | name | 創建時間(事務ID) | 刪除時間(事務ID) |
---|---|---|---|
1 | shenjian | 1 | undefinded |
2 | zhangsan | 1 | undefinded |
3 | lisi | 1 | undefinded |
再假設:假設在執行事務ID為2的過程中,剛執行到了第一步:1,假設事務執行完事務3後,接著又執行了事務4;
事務4:
st art transaction;
delete from tb where id=1;
commit;
此時數據庫中的表為:
id | name | 創建時間(事務ID) | 刪除時間(事務ID) |
---|---|---|---|
1 | shenjian | 1 | 4 |
2 | zhangsan | 1 | undefinded |
3 | lisi | 1 | undefinded |
4 | wangwu | 3 | undefinded |
接著執行事務ID為2的第二步:2,根據select 檢索條件可以知道,它會檢索創建時間(創建事務的ID)小於當前事務ID的行和刪除時間(刪除事務的ID)大於當前事務的行,而id=4的行上面已經說過,而id=1的行由於刪除時間(刪除事務的ID)大於當前事務的ID,所以事務2的第二步:2 select * from tb 會把id=1的數據檢索出來.所以,事務2中的兩條select 語句檢索出來的數據都如下:
id | name | 創建時間(事務ID) | 刪除時間(事務ID) |
---|---|---|---|
1 | shenjian | 1 | 4 |
2 | zhangsan | 1 | undefinded |
3 | lisi | 1 | undefinded |
update:
InnoDB執行UPDATE,實際上是新插入了一行記錄,並保存其創建時間為當前事務的ID,同時保存當前事務ID到要UPDATE的行的刪除時間.
再再假設:
假設在執行完事務2的第一步:1 後又執行,其它用戶執行了事務3,4,這時,又有一個用戶對這張表執行了UPDATE操作:
第5個事務:
start transaction;
update tb set name='xxx' where id=2;
commit;
按照update的更新原則:會生成新的一行,並在原來要修改的列的刪除時間列上添加本事務ID,得到表如下:
id | name | 創建時間(事務ID) | 刪除時間(事務ID) |
---|---|---|---|
1 | shenjian | 1 | 4 |
2 | zhangsan | 1 | 5 |
3 | lisi | 1 | undefinded |
4 | wangwu | 3 | undefinded |
2 | xxx | 5 | undefinded |
繼續執行事務2第二步:2,根據select 語句的檢索條件(創建時間事務ID小於自己查詢事務ID的,和刪除時間事務ID大於自身的查詢事務ID),得到下表:
id | name | 創建時間(事務ID) | 刪除時間(事務ID) |
---|---|---|---|
1 | shenjian | 1 | 4 |
2 | zhangsan | 1 | 5 |
3 | lisi | 1 | undefinded |
並得不到上上個表的ID=4的第四行(因為創建事務ID=3 > 查詢事務ID=2) 和第五行Update 事務創建的ID=2 的更新數據(因為 創建他的Update 事務ID=5 > 查詢事務ID=2)
Mysql 中的MVCC原理,undo日誌的依賴