「MySQL高級篇」MySQL之MVCC實現原理&&事務隔離級別的實現

語言: CN / TW / HK

theme: qklhk-chocolate highlight: solarized-light


攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第2天,點擊查看活動詳情

大家好,我是melo,一名準大三後台練習生,臨近開學,死去的MySQL突然開始拷打我🤣🤣🤣!

🍳引言

MVCC,非常順口的一個詞,翻譯起來卻不是特別順口:多版本併發控制。

  • 其中多版本是指什麼呢?一條記錄的多個版本。
  • 併發控制?如何實現呢?我們上篇剛講到了鎖機制,而MVCC則是用更好的方式來提高併發性能,避免加鎖!具體如何實現,底層原理是什麼,這篇將帶你攻破ta。

🎏本篇速覽腦圖

MVCC.png

image.png
通過「版本鏈」來控制併發事務訪問同一個記錄時的行為就叫 MVCC(多版本併發控制)。

看完後文,再回過頭來看這張圖,就會理解了

當前讀,快照讀

首先我們需要一些前置知識,區分開當前讀和快照讀。

  1. 加鎖的讀,則是當前讀,另外update,insert,delete也都是當前讀
  2. 快照讀,我們平時簡單的select語句其實就是【不加鎖】

    注意串行化隔離級別下,快照讀會退化為當前讀。

image.png

  • 那這倆跟MVCC有什麼關係呢?

快照讀,相當於你可以讀到的是一個歷史版本,維護這些歷史版本就需要MVCC出馬了【其中的undolog版本鏈】

MVCC用處

解決 讀—寫 衝突的無鎖併發控制,每次對A記錄的寫操作,都會給A保存一個快照版本,至於讀操作的時候,讀的是哪個快照版本,這就得看MVCC的實現原理了【下文的readview訪問規則】

🎯MVCC實現原理

🎯記錄中的隱藏字段

InnoDB 裏面每個事務有一個唯一的事務 ID,叫作 transaction id。它是在事務開始的時候向 InnoDB 的事務系統申請的,是按申請順序嚴格遞增的

每行數據也都是有多個版本的。每次事務更新數據的時候,都會生成一個新的數據版本,並且把 transaction id 賦值給這個數據版本的事務 ID,記為 row trx_id【也就是下圖的DB_TRX_ID】。同時,舊的數據版本要保留,並且在新的數據版本中,能夠有信息可以直接拿到它。
image.png

  • DB_TRX_ID(6字節):表示最後一次插入或更新該行的事務 id。此外,delete 操作在內部被視為更新,只不過會在記錄頭 Record header 中的 deleted_flag 字段將其標記為已刪除

  • DB_ROLL_PTR(7字節) 回滾指針,指向該行的 undo log 。如果該行未被更新,則為空

  • DB_ROW_ID(6字節):如果沒有設置主鍵且該表沒有唯一非空索引時,InnoDB 會使用該 id 來生成聚簇索引

🎯readview

四個核心字段

image.png
計算m_ids的時候,可能會有新的事務產生,為了防止這種情況出現,MySQL保證計算m_ids【也就是生成視圖數組的時候】會在事務系統的鎖保護下進行,是原子操作,期間不會創建新的事務。

🎈🎈訪問規則

image.png

  • 如果記錄的 trx_id 值小於 Read View 中的 min_trx_id 值,表示這個版本的記錄是在創建 Read View 已經提交的事務生成的,所以該版本的記錄對當前事務可見

  • 如果記錄的 trx_id 值大於等於 Read View 中的 max_trx_id 值,表示這個版本的記錄是在創建 Read View 才啟動的事務生成的,所以該版本的記錄對當前事務不可見

  • 如果記錄的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之間,表明這個版本的記錄在創建 Read View 的時候 可能處於“活動狀態”或者“已提交狀態”;需要判斷 trx_id 是否在 m_ids 列表【活躍狀態】中:--【因為是有序的,故採用二分查找】

  • 如果記錄的 trx_id m_ids 列表中,表示生成該版本記錄的活躍事務依然活躍着(還沒提交事務),所以該版本的記錄對當前事務不可見
  • 如果記錄的 trx_id 不在 m_ids列表中,表示生成該版本記錄的活躍事務已經被提交,所以該版本的記錄對當前事務可見

🍔🍔總結

  1. 版本未提交,不可見;
  2. 版本已提交,但是是在視圖創建後提交的,不可見;
  3. 版本已提交,而且是在視圖創建前提交的,可見。

🎈🎈update特例

image.png
在這個例子中,如果還按上邊的訪問規則來看的話,應該是讀取不到102這個版本來着,但實際情況是如何呢?

如果讀取不到的話:那事務B還是在原來的k基礎上去+1,那麼事務C的更新相當於是丟失了!

這裏就涉及到了我們開篇講到的當前讀,更新數據都是先讀後寫的,這個讀,就是“當前讀”。

而且當前讀需要對數據行加鎖,此處由於事務C已經提交了,釋放了鎖【兩階段協議】,因此事務B可以直接查到,若事務C還未提交的話,還需要阻塞等待。

🤷‍♂️🤷‍♂️45講疑問

可能看了45講的小夥伴會有疑問,45講裏邊這個圖

這樣,對於當前事務的啟動瞬間來説,一個數據版本的 row trx_id,有以下幾種可能:

  1. 如果落在綠色部分,表示這個版本是已提交的事務或者是當前事務自己生成的,這個數據是可見的;
  2. 如果落在紅色部分,表示這個版本是由將來啟動的事務生成的,是肯定不可見的;
  3. 如果落在黃色部分,那就包括兩種情況
    a. 若 row trx_id 在數組中,表示這個版本是由還沒提交的事務生成的,不可見;
    b. 若 row trx_id 不在數組中,表示這個版本是已經提交了的事務生成的,可見。

這個圖很容易迷惑到我們,讓我們誤以為黃色部分跟未提交事務集合是等同的,那怎麼落在黃色部分裏邊,還能再細分成兩種情況呢?

melo畫了個花裏胡哨的圖,來看看計算的過程【如有錯誤之處還請指正】

  1. 1-10就是45講裏邊的綠色部分,11-15是黃色部分,15之後是紅色部分
  2. 如此可以看到,黃色部分裏邊,還是有一些不在m_ids裏邊的吧,不要被表面的圖像迷惑了
  3. 並不是説只有11之前的,才是已提交事務,11-15裏邊也是可能會有已提交事務的

image.png

生成時機

注意,並不是開啟事務就生成了,得執行快照讀了才會

RC: 在事務中每一次執行快照讀都會生成
RR:僅在事務中第一次執行快照時生成,後續都是複用這個readview
但是如果事務中進行了當前讀的操作,比如事務中進行了update操作,後續再查詢就會重新生成ReadView

其實就是上邊的update特例

🎯undo log

當讀取記錄時,若該記錄被其他事務佔用或當前版本對該事務不可見,則可以通過 undo log 讀取之前的版本數據,以此實現快照讀

類型

image.png
在 InnoDB 存儲引擎中 undo log 分為兩種: insert undo log 和 update undo log:

  1. insert undo log :指在 insert 操作中產生的 undo log。因為 insert 操作的記錄只對事務本身可見【只在事務回滾時需要】,對其他事務不可見,故該 undo log 可以在事務提交後直接刪除。不需要進行 purge 操作
  2. update undo log :update 或 delete 操作中產生的 undo log。該 undo log可能需要提供 MVCC 機制,因此不能在事務提交時就進行刪除。提交時放入 undo log 鏈表【下文的版本鏈】,等待 purge線程 進行最後的刪除

🎈版本鏈

類似一個鏈表,通過回滾指針,串聯起來

  • 鏈表頭部是最新的數據,尾部是最舊的記錄

image.png

🍔栗子

🎈🎈RC的例子

快照讀

image.png
先看事務5裏邊,兩次快照讀生成的readview是怎樣的?

  1. 第一次執行,此時活躍的事務id有【3,4,5】(2已經提交了)
  2. 最小即是3,最大【注意是預分配最大】是6
  3. 創建該事務的id自然是5

    第二次快照讀也是同樣的分析方式

🎈判斷能查到哪個事務記錄

我們想知道第一次快照讀,讀取到的是哪個事務對應的記錄【左下角中四個記錄】

比如拿 0x0003這條記錄來分析,trx_id是3,去跟第一個readview比對

  1. 判斷是否是當前事務創建的記錄,3!=5,説明不是
  2. 判斷是否已經提交了【小於min_trx_id】,3不小於3,則還未提交
  3. 判斷是否是創建readview之後才創建的事務記錄【大於max_trx_id】,3不大於,則不是
  4. 判斷數據是否已經提交【不在m_ids】裏邊,3在説明還未提交

    因此,第一次快照讀,是沒法讀取到 0x0003這條記錄的

image.png

RR的例子

image.png
具體如何分析,跟上邊RC是一樣的,這裏就不再贅述

只需要注意:如果期間出現了當前讀,則會重新生成readview

總結

MVCC就是為快照讀而生的,維護不同的快照版本,使得不同事務的讀-寫操作不會衝突,實現多版本併發控制,藉助MVCC,數據庫可以實現READ COMMITTED,REPEATABLE READ等隔離級別

MVCC.png

💠下篇預告

這篇我們主要講的是MVCC多版本併發控制,結合了事務的隔離級別,而關於事務背後的原理相關的日誌,這些我們留到後邊再來詳解。

🖨參考文獻

  • MySQL45講
  • 黑馬MySQL視頻

收藏=白嫖,點贊+關注才是真愛!!!本篇文章如有不對之處,還請在評論區指出,歡迎添加我的微信一起交流:Melo__Jun

🧿友鏈