MVCC簡述
MVCC(Mutil-Version Concurrency Control)
,就是多版本併發控制。MVCC 是一種併發控制的方法,一般在數據庫管理系統中,實現對數據庫讀寫的併發訪問
。
在Mysql的InnoDB
引擎中就是指在已提交讀(READ COMMITTD)
和可重複讀(REPEATABLE READ)
這兩種隔離級別下的事務對於SELECT
操作會訪問版本鏈中的記錄
的過程。這就使得別的事務可以修改這條記錄
,反正每次修改都會在版本鏈中記錄
。SELECT
可以去版本鏈中讀取記錄
,這就實現了讀-寫,寫-讀的併發執行
,提升了系統的性能。
當前讀和快照讀
在MVCC併發控制中,讀操作可以分成兩類:
快照讀
(snapshot read)
讀取的是記錄的可見版本
(有可能是歷史版本),不用加鎖
。簡單的select操作,屬於快照讀。當前讀
(current read)
讀取的是記錄的最新版本
,並且當前讀返回
的記錄
,都會加上鎖
,保證其他事務不會再併發修改這
條記錄。插入/更新/刪除
操作,屬於當前讀。
一致性非鎖定讀
一致性非鎖定讀
(consistent nonlocking read)是指InnoDB
存儲引擎通過多版本控制
(MVCC)讀取
當前數
據庫中行數據的方式
。
如果讀取的行正在執行DELETE或UPDATE
操作,這時讀取操作不會
因此去等待行鎖的釋放
。相反地,InnoDB
會去讀取行的一個最新可見快照
。
若出現AB會話如下圖所示:
當會話B提交事務後,會話A再次運行 SELECT * FROM test WHERE id = 1 的SQL語句
時,兩個不同的事務隔離級別
下得到的結果可能就不一樣
了。
Mysql實現MVCC
就依賴
的undo log
以及read view
。
版本鏈
在InnoDB引擎表
中,它的聚簇索引
記錄中有兩個
必要的隱藏列
:
- trx_id
這個id用來存儲的每次對某條聚簇索引記錄
進行修改的
時候的事務id
。 - roll_pointer
每次對哪條聚簇索引記錄有修改
的時候,都會把老版本寫入undo日誌
中。這個roll_pointer
就是存了一個指針
,它指向這條聚簇索引記錄的上一個版本的位置
,通過它來獲得上一個版本的記錄信息。(注意插入操作
的undo日誌沒有這個屬性
,因爲它沒有老版本)
事務鏈表
MySQL中的事務
在開始到提交
這段過程
中,都會被保存
到一個叫trx_sys的事務鏈表
中,這是一個基本
的鏈表結構:
ct-trx --> trx11 --> trx9 --> trx6 --> trx5 --> trx3;
事務鏈表
中保存的都是還未提交的事務
,事務一旦被提交
,則會被從事務鏈表中摘除
。
RR隔離級別
下,在每個事務
開始的時候,會將當前系統中的所有的活躍事務拷貝
到一個列表中(read view
)RC隔離級別
下,在每個語句
開始的時候,會將當前系統中的所有的活躍事務拷貝
到一個列表中(read view
)
show engine innodb status
,就能夠看到事務列表。
ReadView
ReadView中主要就是有個列表
來存儲
我們系統中當前活躍着的讀寫事務
,也就是begin了還未提交的事務。通過這個列表來判斷記錄的某個版本是否對當前事務可見
。假設當前列表裏的事務id爲[80,100]。
- 如果訪問的記錄版本的事務id爲
50
,比當前列表最小的id80小
,那說明這個事務在之前就提交
了,所以對當前活動的事務來說是可訪問的
。 - 如果訪問的記錄版本的事務id爲
90
,發現此事務在列表id最大值和最小值之間
,那就再判斷一下是否在列表內
,如果在那就說明此事務還未提交
,所以版本不能被訪問
。如果不在
那說明事務已經提交
,所以版本可以被訪問
。 - 如果訪問的記錄版本的事務id爲
110
,那比事務列表最大id100都大
,那說明這個版本
是在ReadView生成之後才發生
的,所以不能被訪問
。
這些記錄都是去版本鏈裏面找的
,先找最近記錄
,如果最近這一條記錄事務id不符合條件,不可見的話
,再去找上一個版本
再比較當前事務的id和這個版本事務id看能不能訪問,以此類推直到返回可見的版本或者結束
。
參考:https://baijiahao.baidu.com/s?id=1629409989970483292&wfr=spider&for=pc