1 MVCC基本原理
MVCC:多版本并發控制(MVCC,Multiversion Currency Control)。一般情況下,事務性儲存引擎不是只使用表鎖,行加鎖的處理數據,而是結合了MVCC機制,以處理更多的并發問題。Mvcc處理高并發能力最強,
但系統開銷 比最大(較表鎖、行級鎖),這是最求高并發付出的代價。
** InnoDB實現MVCC的方法是,它存儲了每一行的三個額外的隱藏字段:**
1.DB_TRX_ID:一個6byte的標識,每處理一個事務,其值自動+1
#下面提到的“創建時間”和“刪除時間”記錄的就是這個DB_TRX_ID的值
#如insert、update、delete操作時,刪除操作用1個bit表示。
#DB_TRX_ID是最重要的一個,可以通過語句“show engine innodb status”來查找 2.DB_ROLL_PTR: 大小是7byte,指向寫到rollback segment(回滾段)的一條undo log記錄(update操作的話,記錄update前的ROW值)3.DB_ROW_ID: 大小是6byte,該值隨新行插入單調增加。#當由innodb自動產生聚集索引時聚集索引(即沒有主鍵時,因為MYSQL默認聚簇表,會自動生成一個ROWID)#包括這個DB_ROW_ID的值,#不然的話聚集索引中不包括這個值,這個用于索引當中。
DB_TRX_ID記錄了行的創建的時間刪除的時間在每個事件發生的時候,每行存儲版本號,而不是存儲事件實際發生的時間。每次事物的開始這個版本號都會增加。自記錄時間開始,每個事物都會保存記錄的系統版本號。
依照事物的版本來檢查每行的版本號。在insert操作時 “創建時間”=DB_TRX_ID,這時,“刪除時間”是未定義的;在update時,復制新增行的“創建時間”=DB_TRX_ID,刪除時間未定義,舊數據行“創建時間”不變,
刪除時間=該事務DB_TRX_ID;delete操作,相應數據行的“創建時間”不變,刪除時間=該事務的DB_ROW_ID;select操作對兩者都不修改,只讀相應的數據
MVCC結合隔離級別:
1.READ UNCOMMITTED ,不適用MVCC讀,可以讀到其他事務修改甚至未提交的2.READ COMMITTED ,其他事務對數據庫的修改,只要已經提交,其修改的結果就是可見的,與這兩個事務開始的先后順序無關,不完全適用于MVCC讀,3.REPEATABLE READ,可重復讀,完全適用MVCC,只能讀取在它開始之前已經提交的事務對數據庫的修改,在它開始以后,所有其他事務對數據庫的修改對它來說均不可見4.SERIALIZABLE ,完全不適合適用MVCC,這樣所有的query都會加鎖,再它之后的事務都要等待
MVCC只工作在REPEATABLE READ和READ COMMITED隔離級別下
2 REPEATABLE READ 可重復讀下的MVCC
##### SELECT
Innodb檢查沒行數據,確保他們符合兩個標準:
1.InnoDB只查找版本早于當前事務版本的數據行(也就是數據行的版本必須小于等于事務的版本),這確保當前事務讀取的行都是事務之前已經存在的,或者是由當前事務創建或修改的行2.行的刪除操作的版本一定是未定義的或者大于當前事務的版本號。確定了當前事務開始之前,行沒有被刪除 符合了以上兩點則返回查詢結果。
INSERT
InnoDB為每個新增行記錄當前系統版本號作為創建ID。
DELETE
InnoDB為每個刪除行的記錄當前系統版本號作為行的刪除ID。
UPDATE
InnoDB復制了一行。這個新行的版本號使用了系統版本號。它也把系統版本號作為了刪除行的版本。
3 MVCC深入
如果根據事務DB_TRX_ID去比較獲取事務的話,按道理在一個事務B(在事務A后,但A還沒commit)select的話 B.DB_TRX_ID>A.DB_TRX_ID則應該能返回A事務對數據的操作以及修改。那不是和前面矛盾?其實不然。
InnoDB每個事務在開始的時候,會將當前系統中的活躍事務列表(trx_sys->trx_list)創建一個副本(read view),然后一致性讀去比較記錄的tx id的時候,并不是根據當前事務的tx id,而是根據read view最早一個事務的tx id(read view->up_limit_id)來做比較的,這樣就能確保在事務B之前沒有提交的所有事務的變更,B事務都是看不到的。當然,這里還有個小問題要處理一下,就是當前事務自身的變更還是需要看到的。