簡述 InnoDB 對 MVCC 的實現

一、簡述

分爲兩個要點簡述:

要點一:行記錄的歷史版本是什麼樣子的?
InnoDB 將行記錄及其歷史行記錄通過隱藏字段(DATA_ROLL_PTR)鏈成一個鏈表。歷史行記錄其實就是 undo log,放在共享表空間的 undo 段。

要點二:當前事務進行快照讀時,如何選擇歷史版本?
每個行記錄及其歷史行記錄都有一個隱藏字段(DATA_TRX_ID),記錄了最後更新該行記錄的事務 ID。
事務的事務 ID 由 InnoDB 統一分配,越是後分配,事務 ID 越大,即事務 ID 是單增的。
RR 級別下,事務在第一個快照讀時生成一個 read view。
RC 級別下,事務在每一次快照讀都重新生成一次 read view。
每次事務進行快照讀時,都會根據 read view,按照只能讀取在創建 read view 之前已提交事務所做的修改的原則,選擇最新的符合要求的歷史行記錄讀取。

二、詳述

2.1 undo log

同一行記錄各個版本構成的鏈表,示意圖(圖片非原創):
示意圖
事務每對行記錄 update 一次,就會產生一個歷史行記錄。

一個可以加深理解的點是:行記錄的 undo 鏈表,尾結點一定是某個事務對該記錄的 insert 操作,中間結點都是事務對該記錄的 update 操作,頭結點可能是事務的 update 操作,也可能是事務的 delete 操作。

每條記錄的頭信息(record header)裏都有一個專門的 bit(deleted_flag)來表示當前記錄是否已經被刪除,在 delete 行記錄時,不會立即刪除該記錄,而是僅將 deleted_flag 置 1。當沒有任何活躍事務的快照讀需要依賴該記錄時,該記錄會被 InnoDB 的 purge 線程真正刪除。

2.2 選擇歷史版本的具體實現

InnoDB 會維護一個所有當前活躍事務 ID 的列表。

當事務需要建立 read view 時:

  • 會拷貝一份 InnoDB 維護的活躍事務 ID 列表,記作 trx_ids。
  • 保存 InnoDB 準備分配的下一個事務 ID(大於當前所有事務 ID,包括已提交的事務),記作 low_limit_id。
  • 保存當前 read view 中最小的事務 ID,記作 up_limit_id。

有了這三樣東西,再加上只能讀取在創建 read view 之前提交事務所做的修改的原則,估計大家已經知道是怎麼選擇判斷的了。

選擇歷史版本的流程:
從頭結點依次向後判斷,因爲越在前面的記錄越新,所以返回第一個符合要求的版本。
如果頭結點就滿足要求,還需要特判一下記錄是否被刪除,如果被刪除,則返回空。

判斷是否符合要求的流程:
取出該版本的事務 ID,判斷該事務在創建 read view 時是否已提交。
拿該事務 ID 和 up_limit_id 比較,如果小於,則可以讀取。
再拿該事務 ID 和 low_limit_id 比較,如果大於等於,說明該事務是創建 read view 後,新開啓的事務,不可以讀取。
最後判斷該事務 ID 是否在 trx_ids 裏,如果不在,則可以讀取,否則不可以讀取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章