1 為什么需要MVCC
用來進行事務回滾操作;
有事務存在讀寫沖突時,也能做到不加鎖,非阻塞并發讀
2 undolog
2.1 undolog定義
在InnoDB中的每一條記錄實際都會存在三個隱藏列:
DB_TRX_ID:事務 ID,是根據事務產生時間順序自動遞增的,是獨一無二的。如果某個事務執行過程中對該記錄執行了增、刪、改操作,那么InnoDB存儲引擎就會記錄下該條事務的 id。
DB_ROLL_PTR:回滾指針,本質上就是一個指向記錄對應的undo log的一個指針,InnoDB 通過這個指針找到之前版本的數據
DB_ROW_ID:主鍵,如果有自定義主鍵,那么該值就是主鍵;如果沒有主鍵,那么就會使用定義的第一個唯一索引;如果沒有唯一索引,那么就會默認生成一個隱藏列作為主鍵。
版本V1、V2并不是物理上真實存在的,而是每次需要的時候根據當前版本和undo log計算出來的。比如,需要V1的時候,就是通過V3依次執行U2、U1算出來。
可以將這些 undo 日志都連起來,串成一個鏈表,形成版本鏈。版本鏈的頭節點就是當前記錄最新的值。
2.2 undo log分類
Insert undo log :insert生成的日志,僅在事務回滾中需要,并且可以在事務提交后立即丟棄。
Update undo log:update/delete生成的日志,除了用于事務回滾,還用于一致性讀取,只有不存在innodb為其分配快照的事務之后才能丟棄它們,在一致讀取中可能需要update undo log中的信息來構建數據庫行的早期版本。
2.3 數據刪除
刪除操作實際上不會直接刪除,而只是標記為刪除,最終的刪除操作是purge線程完成的
purge線程作用
1、清理undo log
2、清除page里面帶有Delete_Bit標識的數據行。在InnoDB中,事務中的Delete操作實際上并不是真正的刪除掉數據行,而是一種Delete Mark操作,在記錄上標識刪除,真正的刪除工作需要后臺purge線程去完成。
2.4 更新主鍵
聚簇索引和二級索引都無法進行in place update,都會產生兩個版本
update分兩步執行,先刪除該行,再插入一行目標行
2.5 更新非主鍵
聚簇索引可以in place update,二級索引產生兩個版本
聚簇索引記錄undo log,二級索引不記錄undo log
更新二級索引,同時需要判斷是否修改索引頁面的MAX_TRX_ID
2.6 刪除操作
刪除操作實際上不會直接刪除,而只是標記為刪除,最終的刪除操作是purge線程完成的
3 Read View
Read View是InnoDB在實現MVCC時用到的一致性讀視圖,用于支持讀提交和可重復讀隔離級別的實現,作用是執行期間判斷版本鏈中的哪個版本是當前事務可見的。
本質上是InnoDB為每個事務構造了一個數組,用來保存當前正在活躍(啟動了但還沒提交)的所有事務ID。
數組里面事務ID的最小值記為低水位,當前系統里面已經創建過的事務ID的最大值加1記為高水位;這個視圖數組和高水位,就組成了當前事務的一致性視圖(read-view)。
對于以上事務:
如果在「已提交事務」部分,表示這個版本是已提交的事務或者是當前事務自己生成的,這個數據對當前事務是可見的;
如果落在「未開啟事務」部分,表示這個版本是由將來啟動的事務生成的,是肯定不可見的;
如果落在「未提交」部分,那就包括兩種情況
a. 若當前版本的trx_id在一致性試圖中,表示這個版本是由還沒提交的事務生成的,不可見;
b. 若當前版本的trx_id不在一致性試圖中,表示這個版本是已經提交了的事務生成的,可見。
3 MVCC的工作原理
3.1 MVCC查詢的工作流程
3.1.1 查詢主鍵索引
生成Read View讀視圖
通過主鍵查找記錄,根據記錄里的DB_TRX_ID與Read View讀視圖進行可見性判斷
配合DB_ROLL_PTR回滾指針和undo log來找到當前事務可見的數據記錄
3.1.2 查詢二級索引
由于二級索引由于沒有三個隱藏列(DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID)如何實現一致讀,可重復讀?
更新二級索引列時,舊的二級索引記錄將被刪除標記(并非真正的刪除),新記錄將被插入;
如果二級索引記錄被標記為刪除,或者二級索引頁被更新的事務更新,則不使用覆蓋索引技術(要通過聚族索引查找正確版本)。
如果啟用了索引條件下推(ICP)優化,首先會通過索引下推過濾掉不符合要求的行,來避免使用聚集索引查找。如果找到匹配的記錄,即使在刪除標記的記錄中,InnoDB也會在聚集索引中查找該記錄。
具體查詢步驟:
生成Read View讀視圖
-
比較讀視圖的up_limit_id與MAX_TRX_ID大小
如果MAX_TRX_ID **小于 **本次Read View的up_limit_id,則全部可見,過濾記錄中的有效記錄
否則,無法通過二級索引判斷可見性,需要一次遍歷每條記錄,反查到聚簇索引記錄,通過聚簇索引記錄來判斷可見性
3.2 MVCC與隔離級別
MVCC 只在 **Read Commited(讀已提交) 和 Repeatable Read(可重讀讀) **兩種隔離級別下工作。
在RC隔離級別下,是每個快照讀都會生成并獲取最新的Read View,這就是我們在RC級別下的事務中可以看到別的事務提交的更新的原因
在RR隔離級別下,則是同一個事務中的第一個快照讀才會創建Read View, 之后的快照讀獲取的都是同一個Read View,從而做到可重復讀
3.3 mvcc能否解決幻讀
幻讀:在一次事務里面,多次查詢之后,結果集的個數不一致的情況叫做幻讀。而多出來或者少的哪一行被叫做幻行。
在快照讀讀情況下,mysql通過mvcc來避免幻讀。
在當前讀讀情況下,mysql通過next-key來避免幻讀。
不能把快照讀和當前讀得到的結果不一樣這種情況認為是幻讀,這是兩種不同的使用。所以mysql的rr級別是解決了幻讀的。