多版本並行控制技術(Multiversion Concurrency Control,MVCC)
技術是為了解決問題而生的,通過 MVCC 我們可以解決以下幾個問題:
MVCC 是通過資料行的歷史版本來實現資料庫的並行控制。
簡單來說 MVCC 的思想就是儲存資料的歷史版本。這樣一個事務進行查詢操作時,就可以通過比較版本號來判斷哪個較新的版本對當前事務可見。
MVCC 沒有正式的標準,所以在不同的 DBMS 中,MVCC 的實現方式可能是不同的。
InnoDB 對 MVCC 的實現主要是通過 版本鏈 + ReadView 結構完成。
先介紹聚簇索引記錄的隱藏列,再介紹 Undo Log 版本鏈
對於使用 InnoDB 儲存引擎的表來說,它的聚簇索引記錄中都包含 3 個隱藏列
事務ID
事務執行過程中,只有在第一次真正修改記錄時(比如進行 insert、delete、update 操作),才會被分配一個唯一的、單調遞增的事務 ID,如果沒有修改記錄操作,按照一定的策略分配一個比較大的事務 ID,減少分配事務 ID 的鎖競爭。每當事務向資料庫寫入新內容時, 所寫的資料都會被標記操作所屬的事務的事務ID。
在 InnoDB 儲存引擎中,版本鏈由資料行的 Undo Log 組成。
每次對資料行進行修改,都會將舊值記錄到 Undo Log,算是該資料行的一箇舊版本。
Undo Log 有兩個重要的屬性:db_roll_ptr、db_trx_id
Undo Log 也有一個 db_roll_ptr 屬性(insert 操作對應的 Undo Log 沒有 db_roll_ptr 屬性,因為 insert 操作對應的資料行沒有更早的版本),Undo Log 的 db_roll_ptr 屬性指向上一次操作的 Undo Log,所有的版本被 db_roll_ptr 屬性連線形成一個連結串列。該連結串列即版本鏈,版本鏈的頭節點就是資料行的最新值。
Undo Log 還包含生成該版本時,對應的事務 ID,用於判斷當前版本的資料對事務的可見性。
版本鏈如下圖所示。這樣如果我們想要查詢歷史快照,就可以通過遍歷回滾指標的方式進行查詢。
ReadView 用來判斷版本鏈中的哪個較新的版本對當前事務是可見的。
ReadView 中主要包含 4 個比較重要的屬性:
有了這個 ReadView,這樣在存取某條記錄時,就可以用 ReadView 來判斷版本鏈中的哪個較新的版本對當前事務是可見的。
如果被存取版本的 transaction_id 屬性值與 ReadView 中的 creator_trx_id 值相同,表明當前事務在存取它自己修改過的記錄,所以該版本可以被當前事務存取。
如果被存取版本的 transaction_id 屬性值 小於 ReadView 中的 min_trx_id 值,表明生成該版本的事務在當前事務生成 ReadView 前已經提交了,所以該版本可以被當前事務存取。
如果被存取版本的 transaction_id 屬性值 大於 ReadView 中的 max_trx_id 值,表明生成該版本的事務在當前事務生成 ReadView 後才開啟,所以該版本不可以被當前事務存取。
如果被存取版本的 transaction_id 屬性值在 ReadView 的 min_trx_id 和 max_trx_id 之間,那就需要判斷一下 transaction_id 屬性值是不是在 m_ids 列表中:
如果某個版本的資料對當前事務不可見的話,那就順著版本鏈找到下一個版本的資料,繼續按照上邊的步驟判斷
可見性,依此類推,直到版本鏈中的最後一個版本。如果最後一個版本也不可見的話,那麼就意味著該條記錄對當前事務完全不可見,查詢結果就不包含該記錄。
MVCC 可以防止髒讀,也可以防止不可重複讀。
防止髒讀 和 防止不可重複讀 實現的不同之處就在:ReadView 的生成時機不同
對於隔離級別為 讀未提交 的事務來說,直接讀取記錄的最新版本即可。
對於隔離級別為 序列化 的事務來說,InnoDB 儲存引擎使用加鎖的方式來存取記錄。
對於隔離級別為 讀已提交 和 可重複讀 的事務來說,都必須保證只能讀到已經提交的事務修改的資料,不能讀到未提交的事務修改的資料。
本文來自部落格園,作者:真正的飛魚,轉載請註明原文連結:https://www.cnblogs.com/feiyu2/p/mvcc.html