一.MVCC是什麼?
MVCC,全稱Multi-Version Concurrency Control,即多版本并發控制。MVCC是一種并發控制的方法,一般在資料庫管理系統中,實作對資料庫的并發通路.
為什麼要有MVCC?
在并發通路場景中,會出現以下三種情況
讀讀、讀寫、寫寫
讀讀不存在安全問題,寫寫必然需要加鎖來保證資料安全,那MVCC其實就是大佬們不滿意簡單加鎖來解決讀寫沖突的一種無鎖手段.
MVCC本質是什麼?
其實就是通過一些手段(隐藏字段,日志,readview)和算法來判斷事務間的可見性,進而解決髒讀,不可重複讀資料問題.
二.前置知識
在介紹MVCC之前,我們應該先清楚以下知識點.
1.并發通路可能帶來的資料問題.
(1)髒讀:在一個事務中讀取了另一個事務尚未送出的資料.
(2)不可重複讀:在一個事務中兩次讀取資料内容不一緻,主要由update引起.
(3)幻讀:在一個事務中的兩次讀取資料數量不一緻,主要由insert和delete引起.
2.讀資料的兩種方式.
(1)目前讀:
讀取的是記錄資料的最新版本,并且目前讀傳回的記錄都會加上鎖,保證其他事務不會再并發的修改這條記錄
以前方式均為目前讀:
select … lock in share mode(會增加表鎖)
select … for update(會增加行鎖)
insert
update
delete
(2)快照讀:
讀取的是記錄資料的可見版本(可能不是最新的資料),不用加鎖
以下方式為快照讀:
簡單的select操作(不包括 select … lock in share mode, select … for update)
3.資料庫的隔離級别:
mysql中預設的隔離級别是RR,也就是可重複讀.
三.MVCC組成.
MVCC由隐藏字段,undolog,readview三部分組成.
1.隐藏字段.
(1)DB_TRX_ID
建立這條記錄的事務id或者最後一次修改的事務id.
(2)DB_ROLL_PTR
復原指針,指向這條記錄的上一個版本.
(3)DB_ROW_ID
隐藏主鍵,如果沒有主鍵會生成一個6位元組的row_id.
舉例:有一個user表,表中有三個字段 name,age,gender,現在新增一條name=zhangsan的記錄,資料情況如下:
2.undolog
復原日志,記錄事務中的曆史資料,便于在insert,update,delete操作之後進行復原.
舉例:還是上面那個表,現在将name=zhangsan改成name=lisi,那我們資料庫中的資料則變為.
而曆史資料則會存儲到undolog日志中去,復原指針會指向該資料存儲位置.
注意:在undolog日志中會存儲所有的曆史資料,那豈不是會無限增長?是以會有一個單獨的線程去回收清除不需要的日志
3.readview
事務在進行快照讀的時候産生的一個讀視圖.所謂的讀視圖隻是包含了某些字段資訊而已.
作用:
其實就是為了進行判斷多個事務間的可見性判斷而存在的資訊記錄.
readview包含以下三個字段資訊.:
(1)trx_list:
一個數值清單,用來記錄目前readview生成時刻存在的活躍事務(也就是尚未送出的事務)id.
(2)up_limit_id:
事務活躍清單中最小的事務id.
(3)low_limit_id:
在readview生成時刻尚未配置設定的下一個事務id.
重點:
在不同的隔離級别(RC和RR)下生成的readview的時機是不一樣的.
RC:每次進行快照讀都會生成新的readview.
RR:事務開啟後第一次進行快照讀的時候才會生成readview,以後的快照讀都會使用第一次生成的readview(如果進行目前讀,則會重新生成readview).
四.MVCC核心流程.
首先給一個案例,在RR隔離級别的情況下.
在我進行如下操作的時候,事務t2在進行快照讀的時候能看到t4更新的資料.
但是當我修改事務操作順序,如下圖所示,那麼t2在第二次快照讀就看不到t4更新的資料.
究竟是為什麼呢?下面用mvcc進行刨析,相信看完就會了解.
首先在第一種情況下:
t2在進行快照讀的時候,會生成一個readview,而是否能看到個t4事務的個更新資料取決于可見性算法(下圖黃框中所示).依次執行1,2,3判斷邏輯.可知在t2進行快照讀的時候是能夠看見t4更新的資料的.
第二種情況:
首先在t2進行第一次快照讀的時候,生成的readview如下所示. 這裡也顯示出DB_TRX_ID的值,因為一會要用到.
然後在t4進行事務送出後,會修改這條記錄中DB_TRX_ID的值,修改為4
那麼在t2進行第二次進行快照讀的時候,因為此時的隔離級别是RR,是以不會生成新的readview,會使用第一次生成的readview,此時繼續用可見性算法進行判斷,由第三個邏輯判斷可知,在readview生成時刻,t4事務還是活躍狀态,還沒有送出,那麼此時目前資料是看不到的,也就驗證了前面的觀點.
注意:
在快照讀的時候有時候會讀的是曆史資料,讀的是哪裡的曆史資料?
是undolog日志中的曆史資料
五.如何解決幻讀.
我們知道MVCC可以解決髒讀和不可重複讀,那幻讀要怎麼解決呢?
1.怎樣才會産生幻讀?
在快照讀和目前讀混合使用的時候才會産生幻讀,也就是說當事務中的讀操作全部使用快照讀的時候是不會産生幻讀的.
為什麼?
因為在全部使用快照讀的時候都讀的第一個readview生成的曆史資料,而當進行了目前讀,就會重新生成readview,導緻出現幻讀.
2.解決方式.
在第一次查詢的時候就使用目前讀,會對資料加上行鎖和間隙鎖,這樣其他事務在進行插入的時候會阻塞,進而解決幻讀.