什么是MVVC,為什么要用它
在mysql的InnoDB不同事務隔離級別中,MVVC(Multi-Version Concurrency Control)作業在RC(Read Commited)和RR(Repeatable Read)這兩種隔離級別中,其實MVVC簡單理解,就是通過對每個事務賦予一個唯一的、遞增的id來代替對資料行上鎖從而減小DBMS的開銷,也就是讀不會加鎖,
舉個不是很準確的例子,有兩個事務T1,T2,它們的id分別是1,2,由于事務id是遞增唯一的,因此可以認為T2在邏輯上是后于T1發生的,當T2想要查詢(select)某一行,這行的trx_id(最后修改本行資料的事務id)為1,那么我們就可以認為這一行對T2是可見的,因此回傳改行資料,
其實這個例子問題很大,這也是為什么要寫這篇文章記錄一下
前置知識
在InnoDB中,每個行后面都有一些額外的欄位,主要要知道DB_TRX_ID(最近修改行的事務ID),DB_ROLL_PTR(指向該行被修改的前一個狀態),DELETE_VERSION(行是否被洗掉),
當某個事務T對一行R做update操作時,若R對T是可見的(判斷方法放在后面講),將該行的DELETE_VERSION置為T的id,并將改行放入undo log中,而在表中會插入一條新的行R’,R’的DB_TRX_ID為T的id,DELETE_VERSION為空,DB_ROLL_PTR指向剛剛被打入undo log的那行
修改前↓
修改后↓
READ VIEW
其實ReadView可以理解成資料庫中某個時間戳所有未提交事務的快照,ReadView類有這么幾個引數:
- m_ids:當前時間戳所有未提交的事務
- min_trx_id:m_idx中最小的事務id
- max_trx_id:當前時間戳InnoDB將在下一次分配的事務id
- creator_trx_id:當前事務id
當創建一個ReadView物件時,也就知道了這個時間點上未提交事務的所有資訊,從而能夠通過這些資訊實作鎖的作用甚至增強,
有了這個ReadView,這樣在訪問某條記錄時,只需要按照下邊的步驟判斷記錄的某個版本是否可見:
如果被訪問版本的trx_id屬性值與ReadView中的creator_trx_id值相同,意味著當前事務在訪問它自己修改過的記錄,所以該版本可以被當前事務訪問,
如果被訪問版本的trx_id屬性值小于ReadView中的min_trx_id值,表明生成該版本的事務在當前事務生成ReadView前已經提交,所以該版本可以被當前事務訪問,
如果被訪問版本的trx_id屬性值大于或等于ReadView中的max_trx_id值,表明生成該版本的事務在當前事務生成ReadView后才開啟,所以該版本不可以被當前事務訪問,
如果被訪問版本的trx_id屬性值在ReadView的min_trx_id和max_trx_id之間,那就需要判斷一下trx_id屬性值是不是在m_ids串列中,如果在,說明創建ReadView時生成該版本的事務還是活躍的,該版本不可以被訪問;如果不在,說明創建ReadView時生成該版本的事務已經被提交,該版本可以被訪問,
正題
假如現在DBMS在處理三個事務,T2,T3,T5(T4已經提交了),那么開啟下一個事務的時候,將會分配到一個id為6的id,也就是T6,現在T6要select這樣一行:

首先會創建一個ReadView物件r:
r.m_ids=[T2,T3,T5],
r.min_trx_id=2,
r.max_trx_id=7
由于該行DB_TRX_ID=2, 而2!<r.min_trx_id,并且T2 in m_ids,因此可以判斷出創建快照的時候T2還未提交(當然其實在做這個判斷的時間戳上T2也許已經提交了,不過這種CPU調度的事是無法預料的),因此認為這一行對T6是不可見的,
如果該行是這樣子的:
由于1<r.min_trx_id=2,因此最后修改該行的事務必然已經提交,所以該行對T6可見,
又如果是:
那就最后的改動就是在T6中進行的,也是對T6可見,
亦或是
CPU調度嘛,人家雖然后來的但也可能先你下手了 ,因此由于8>=r.max_trx_id=7,所以該行對T6不可見,找一下DB_ROLL_PTR指向的行,由于是空,回傳0行,(如果不為空,那么對所指向的行對同樣的可見性判斷)
再者:
雖然r.min_trx.id<=4<r.max_trx_id,但是由于T4 not in m_ids,可知T4已提交,因此該行對T6是可見的,
總結一下就是如下一個流程圖:

copy的,tmin,tmax,tid0分別對應r.min_trx.id,r.max_trx_id,DB_TRX_ID,
回到開頭
有些博客寫的是

如果以小于或等于當前事務版本號為依據來判斷行的可見性,那么就會漏掉依然活躍未提交的事務,在RR隔離級別下會產生不可重復度(離譜),
RR和RC的區別
- READ COMMITTED —— 每次讀取資料前都生成一個ReadView
- REPEATABLE READ —— 在第一次讀取資料時生成一個ReadView
Featrue plus
其實在這樣的MVVC下,InnoDB在RR隔離級別下就能避免了幻讀,原因是不管是后面新的事務還是已經存在的未提交事務,他們新增的資料行對當前事務都是不可見的,而已提交的事務又不可能新增行,妙啊
參考博客:
MVCC ReadView介紹
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/203862.html
標籤:其他
上一篇:MySql的簡單應用






