MVCC如何實現數據庫讀已提交和可重複讀這兩種隔離級別?


我們都知道Mysql有四種事務隔離級別:

  • 讀未提交
  • 讀已提交
  • 可重複讀
  • 串行化

這四個隔離級別的特點就不多贅述了,這次主要聊一下MVCC(多版本併發控制)是如何實現“讀已提交”和“可重複讀”這兩種隔離級別的。

在以往的實現中,MySql要使用讀鎖+寫鎖實現隔離級別,而使用MVCC就可以免去讀操作,讀操作直接基於MVCC實現,MVCC到底是啥呢?

MVCC其實主要包含三個概念:隱藏列,undo log,ReadView

隱藏列

在Innodb引擎中,每個數據表都會有兩個隱藏列(其實準確來說是三個,還有一個叫隱藏id,因爲innodb必須要有主鍵,如果建表時沒有顯式指定的話,就會生成這個隱藏id作爲主鍵,當然這個隱藏id和mvcc沒有關係,真正和mvcc有關係的其實是兩個隱藏列),分別是trx_id,創建版本號;和roll_pointer,回滾指針。其中創建版本號其實就是創建該行數據的事務id。這些隱藏列對我們客戶端來說是不可見的。

undo log

當事務對數據行進行一次更新操作時,會把舊數據行記錄在一個叫做undo log的記錄中,在undo log中除了記錄數據行,還會記錄下該行數據的對應的創建版本號,也就是生成這行數據的事務id嘛~然後將原來數據行中的回滾指針指向undo log記錄的這行數據。然後再在原來數據表中進行一次更新操作,如果這次更新操作回滾了,那麼就可以根據回滾指針去undo log中查找之前的數據進行復原。如果後續還有更新操作的話,就會在undo log中和之前的數據行形成一條鏈表,鏈表頭就是最新的數據,這條鏈表就叫做版本鏈

img

(ps:數據本來是劉備,然後事務id爲100的事務先修改成了關羽,再修改成了張飛,後面事務id爲200的事務先修改成了趙雲,再修改成了諸葛亮,)

事務的可見性都是基於這個undo log來實現的

ReadView

剛纔說了更新操作,那查詢操作呢?這纔是實現不同隔離級別的關鍵地方

當進行查詢操作時,事務會生成一個ReadView,ReadView是一個事務快照,準確來說是當前時間點系統內活躍的事務列表,也就是說系統內所有未提交的事務,都會記錄在這個Readview內,事務就根據它來判斷哪些數據是可見的,哪些是不可見的。

查詢一條數據時,事務會拿到這個ReadView,去到undo log中進行判斷。若查詢到某一條數據:

  • 先去查看undo log中的最新數據行,如果數據行的版本號小於ReadView記錄的事務id最小值,就說明這條數據對當前數據庫是可見的,可以直接作爲結果集返回
  • 若數據行版本號大於ReadView記錄最大值,說明這條數據是由一個新的事務修改的,對當前事務不可見,那麼就順着版本鏈繼續往下尋找第一條滿足條件的
  • 若數據行版本號在ReadView最小值和最大值之間,那麼就需要進行遍歷了整個ReadView了,如果數據行版本號等於ReadView的某個值,說說明該行數據仍然處於活躍狀態,那麼對當前事務不可見

讀已提交和可重複讀的實現

ReadView就是這樣來判斷數據可見性的。

那又是如何實現讀已提交和可重複讀呢?其實很簡單,就是生成ReadView的時機不同。

對讀已提交來說,事務中的每次讀操作都會生成一個新的ReadView,也就是說,如果這期間某個事務提交了,那麼它就會從ReadView中移除。這樣確保事務每次讀操作都能讀到相對比較新的數據

而對可重複讀來說,事務只有在第一次進行讀操作時纔會生成一個ReadView,後續的讀操作都會重複使用這個ReadView。也就是說,如果在此期間有其他事務提交了,那麼對於可重複讀來說也是不可見的,因爲對它來說,事務活躍狀態在第一次進行讀操作時就已經確定下來,後面不會修改了。

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