InnoDB的MVCC

什麼是MVCC

MVCC即Multi-Version Concurrency Control,中文翻譯過來叫多版本併發控制。

MVCC解決的問題

MySQl默認的存儲引擎是InnoDB,默認的事務隔離級別是REPEATABLE RED(可重複度),這種事務隔離級別要求2個事務之間不能影響,而且還能支持併發,悲觀鎖無法達到這個性能要求。所以該隔離級別採用的是樂觀鎖的形式實現的,樂觀鎖採用的具體實現方式是:MVCCC

MVCC具體實現

InnoDB的MVCC是通過在每一行記錄的後面保存2個隱藏的列來實現的。這2個列,一個保存了行的創建時間,一個保存了行的過期時間(刪除時間)。這裏儲存的並不是世紀的時間值,而是系統版本號。每開始一個事務,系統版本號自動遞增。事務開始時刻的版本號會作爲當前事務的版本號(也可以理解爲ID)

MVCC具體實現舉例:

創建一張Person表:

create table Person(
id int primary key auto_increment,
name varchar(20)
);

假設系統的版本號從1開始

insert

InnoDB爲新插入的每一行保存當前系統版本號作爲版本號。第一個事務ID爲1:

start transaction
insert into Person values(null,"zs");
insert into Person values(null,"ls");
insert into Person values(null,"ww");
commit

對應在數據中的表如下(後面兩列是隱藏列,我們通過查詢語句並看不到)

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zs 1 undefined
2 ls 1 undefined
3 ww 1 undefined

select

  1. InnoDB只會查找版本早於當前事務版本的數據行(也就是行的系統版本號小於或等於當前事務的版本號),這樣可以確保事務讀取到的行,要麼是事務開始前就已經存在的,要麼是事務自生插入或修改過的。
  2. 行的刪除版本要麼未定義(還沒有數據對他進行刪除操作),要麼大於當前事務版本號(這樣可以確保當前事務讀到的行,在該事務之前未被刪除)。

只有符合上述2個條件的記錄,才能作爲結果返回給查詢

delete

InnoDB會爲刪除的每一行保存當前系統的版本號(事務ID)作爲標識。

第二個事務:ID爲2

start transaction; 
select * from Person; 
select * from Person; 
commit;

假設1:

假設在執行這個事務ID爲2的過程中,剛執行到(1),這時,有另一個事務ID爲3往這個表裏插入了一條數據; 第三個事務ID爲3;

start transaction;
insert into Person values(NULL,'tian');
commit;

這時表中的數據如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zs 1 undefined
2 ls 1 undefined
3 ww 1 undefined
4 tain 3 undefined

然後接着執行事務2中的(2),由於id=4的數據的創建時間(事務ID爲3),執行當前事務的ID爲2,而InnoDB只會查找事務ID小於等於當前事務ID的數據行,所以id=4的數據行並不會在執行事務2中的(2)被檢索出來,在事務2中的兩條select 語句檢索出來的數據如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zs 1 undefined
2 ls 1 undefined
3 ww 1 undefined

假設2

假設在執行這個事務ID爲2的過程中,剛執行到(1),假設事務執行完事務3後,接着又執行了事務4;
第四個事務: ID爲4

start transaction; 
delete from Person where id=1; 
commit;

此時數據庫中的表如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zs 1 4
2 ls 1 undefined
3 ww 1 undefined
4 tain 3 undefined

接着執行事務ID爲2的事務(2),根據SELECT 檢索條件可以知道,它會檢索創建時間(創建事務的ID)小於當前事務ID的行和刪除時間(刪除事務的ID)大於當前事務的行,而id=4的行上面已經說過,而id=1的行由於刪除時間(刪除事務的ID)大於當前事務的ID,所以事務2的(2)select * from yang也會把id=1的數據檢索出來.所以,事務2中的兩條select 語句檢索出來的數據都如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zs 1 4
2 ls 1 undefined
3 ww 1 undefined

UPDATE

InnoDB執行UPDATE,實際上是新插入了一行記錄,並保存其創建時間爲當前事務的ID,同時保存當前事務ID到要UPDATE的行的刪除時間。

假設3:

假設在執行完事務2的(1)後又執行,其它用戶執行了事務3,4,這時,又有一個用戶對這張表執行了UPDATE操作:
第5個事務:ID爲5

start transaction; 
update Person set name='Long' where id=2;
commit;

根據update的更新原則:會生成新的一行,並在原來要修改的列的刪除時間列上添加本事務ID,得到表如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zs 1 4
2 ls 1 5
3 ww 1 undefined
4 tain 3 undefined
2 Long 5 undefined

繼續執行事務2的(2),根據select 語句的檢索條件,得到下表:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zs 1 4
2 ls 1 5
3 ww 1 undefined

還是和事務2中(1)select 得到相同的結果.

參考文章:
https://www.cnblogs.com/pautcher/p/7723422.html
https://segmentfault.com/a/1190000009374567
參考書籍:《高性能MySQL》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章