全網最全的一篇資料庫MVCC詳解,不全我負責

coldplay.xixi
發布: 2020-09-16 16:37:06
轉載
43795 人瀏覽過


全網最全的一篇資料庫MVCC詳解,不全我負責

相關學習推薦:mysql教學

什麼是MVCC

全名為Multi-Version Concurrency Control,即多版本並發控制,主要是為了提高資料庫的並發效能。以下文章都是圍繞著InnoDB引擎來講,因為myIsam不支援事務。

同一行資料平時發生讀寫請求時,會上鎖阻塞住。但mvcc用更好的方式去處理讀取—寫請求,做到在發生讀取—寫請求衝突時不用加鎖

這個讀是指的快照讀,而不是目前讀,目前讀是一種​​加鎖操作,是悲觀鎖定

那它到底是怎麼做到讀—寫不用加鎖的,快照讀目前讀又是什麼鬼,跟著你們的貼心老哥,繼續往下看。

全網最全的一篇資料庫MVCC詳解,不全我負責

目前讀取、快照讀取都是什麼鬼

什麼是MySQL InnoDB下的目前讀取和快照讀取?

目前讀取

它讀取的資料庫記錄,都是目前最新版本,會對目前讀取的資料進行加鎖,防止其他交易修改資料。是悲觀鎖定的一種操作。

如下操作都是目前讀取:

  • select lock in share mode (共享鎖定)

  • select for update (排他鎖)

  • update (排他鎖)

  • #insert (排他鎖)

  • delete (排他鎖定)

  • 序列化交易隔離等級

#快照讀取

快照讀取的實作是基於多版本並發控制,即MVCC,既然是多版本,那麼快照讀取讀到的數據不一定是當前最新的數據,有可能是先前歷史版本的數據。

如下操作是快照讀:

  • 不加鎖的select操作(註:交易層級不是序列化)

快照讀與mvcc的關係

MVCCC是「維持一個資料的多個版本,讓讀寫操作沒有衝突」的一個抽象概念

這個概念需要具體功能去實現,這個具體實作就是快照讀。 (具體實現下面講)#​​

##聽完

貼心老哥的講解,是不是瞬間茅廁頓開

全網最全的一篇資料庫MVCC詳解,不全我負責
資料庫並發場景

  • #讀-讀:不存在任何問題,也不需要並發控制

  • 讀-寫:有執行緒安全性問題,可能會造成事務隔離性問題,可能遇到髒讀,幻讀,不可重複讀取

  • 寫入-寫入:有執行緒安全問題,可能會有更新遺失問題,例如第一類更新遺失,第二類更新遺失

MVCC解決並發哪些問題?

mvcc用來解決讀取—寫衝突的無鎖定並發控制,就是為交易分配

單向增長時間戳。為每個資料修改保存一個版本,版本與交易時間戳記相關聯

讀取操作

只讀取該交易開始前資料庫快照

解決問題如下:

  • 並發讀-寫入時:可以做到讀取操作不阻塞寫入操作,同時寫入操作也不會阻塞讀取操作。

  • 解決

    髒讀幻讀不可重複讀取等交易隔離問題,但無法解決上面的寫-寫更新丟失問題。

因此有了下面提高並發性能的組合拳

  • MVCC 悲觀鎖定:MVCC解決讀寫衝突,悲觀鎖定解決寫寫衝突

  • MVCC 樂觀鎖定:MVCC解決讀寫衝突,樂觀鎖定解決寫寫衝突

MVCC的實作原理

它的實作原理主要是

版本鏈undo日誌Read View 來實現的

版本鏈

我們資料庫中的每行數據,除了我們肉眼看見的數據,還有幾個

隱藏字段,得開天眼才能看見。分別是db_trx_iddb_roll_pointerdb_row_id

  • db_trx_id

    6byte,最近修改(修改/插入)事務ID:記錄建立這條記錄/最後一次修改該記錄的交易ID

  • db_roll_pointer(版本鏈關鍵)

    7byte,回滾指標,指向這條記錄上一個版本(儲存在rollback segment裡)

  • db_row_id

    6byte,隱含的自增ID(隱藏主鍵) ,如果資料表沒有主鍵,InnoDB會自動以db_row_id產生一個叢集索引

  • 實際上還有一個刪除flag隱藏欄位, 記錄被更新刪除不代表真的刪除,而是刪除flag變了

全網最全的一篇資料庫MVCC詳解,不全我負責

#如上圖,db_row_id#是資料庫預設為此行記錄產生的唯一隱式主鍵db_trx_id是目前操作該記錄的交易ID,而db_roll_pointer是一個回滾指標,用來配合undo日誌,指向上一個舊版

每次對資料庫記錄進行更改,都會記錄一條undo日誌,每個undo日誌也都有一個roll_pointer屬性(INSERT操作對應的undo日誌沒有這個屬性,因為該記錄並沒有更早的版本),可以將這些undo日誌都連起來串成一個鍊錶,所以現在的情況就像下圖一樣:

全網最全的一篇資料庫MVCC詳解,不全我負責

對該記錄每次更新後,都會將舊值放到一個undo日誌中,就算是該記錄的一個舊版本,隨著更新次數的增多,所有的版本都會被roll_pointer屬性連接成一個鍊錶,我們把這個鍊錶稱為版本鏈,版本鏈的頭節點就是目前記錄最新的值。另外,每個版本中還包含產生該版本時對應的事務id,這個資訊很重要,在根據ReadView判斷版本可見性的時候會用到。

undo日誌

Undo log 主要用於記錄資料被修改之前的日誌,在表格資訊修改之前先會把資料拷貝到undo log裡。

交易進行回溯時可以透過undo log 裡的日誌進行資料還原

Undo log 的用途

  • 保證交易進行rollback時的原子性和一致性,當交易進行回溯的時候可以用undo log的資料進行恢復

  • 用於MVCC快照讀取的數據,在MVCC多版本控制中,透過讀取undo log歷史版本資料可以實作不同交易版本號都擁有自己獨立的快照資料版本

undo log主要分為兩種:

  • #insert undo log

    代表交易在insert新記錄時產生的undo log , 只在交易回滾時需要,並且在事務提交後可以被立即丟棄

  • update undo log(主要)

    #交易在進行update或delete時產生的undo log ; 不僅在交易回滾時需要,在快照讀取時也需要;

    所以不能隨便刪除,只有在快速讀取或事務回滾不涉及該日誌時,對應的日誌才會被purge執行緒統一清除

Read View(讀取視圖)

交易進行快照讀取操作的時候生產的讀取視圖(Read View),在該交易執行的快照讀取的那一刻,會產生資料庫系統目前的一個快照

記錄並維護系統目前活躍事務的ID(沒有commit,當每個事務開啟時,都會被分配一個ID, 這個ID是遞增的,所以越新的事務, ID值越大),是系統中目前不應該被本交易看到的其他交易id清單

Read View主要是用來做可見性判斷的, 即當我們某個事務執行快照讀的時候,對該記錄建立一個Read View讀視圖,把它比作條件用來判斷當前事務能夠看到哪個版本的數據,既可能是當前最新的數據,也有可能是該行記錄的undo log裡面的某個版本的數據。

Read View幾個屬性

  • #trx_ids: 目前系統活躍(未提交)事務版本號集合。

  • low_limit_id: 建立目前read view 時「目前系統最大交易版本號 1」。

  • up_limit_id: 建立目前read view 時「系統正處於活躍交易最小版本號

  • creator_trx_id: 建立目前read view的交易版本號;

#Read View可見性判斷條件

  • db_trx_id < up_limit_id || db_trx_id == creator_trx_id(顯示)

    如果資料事務ID小於read view中的最小活躍事務ID,則可以肯定該資料是在目前事務啟動之前就已經存在了的,所以可以顯示

    或資料的事務ID等於creator_trx_id ,那麼說明這個資料就是目前事務自己產生的,自己產生的資料自己當然能看見,所以這種情況下此數據也是可以顯示的。

  • db_trx_id >= low_limit_id(不顯示)

    如果資料事務ID大於read view 中的目前系統的最大交易ID,則表示該資料是在目前read view 建立之後才產生的,所以資料不顯示。如果小於則進入下一個判斷

  • db_trx_id是否在活躍交易(trx_ids)中

    • #不存在:則說明read view產生的時候交易已經commit了,這種情況資料可以顯示

    • 已存在:則代表我Read View生成時刻,你這個事務還在活躍,還沒有Commit,你修改的數據,我當前事務也是看不見的。

全網最全的一篇資料庫MVCC詳解,不全我負責

#MVCC和交易隔離等級

上面所講的Read View用於支援RC(Read Committed,讀取提交)和RR(Repeatable Read,可重複讀取)隔離等級實作

RR、RC生成時機

  • RC隔離等級下,是每個快照讀取都會生成並且取得最新Read View;

  • 而在RR隔離層級下,則是同一個交易中第一個快照讀取才會建立Read View, 之後的快照讀取所取得的都是同一個Read View,之後的查詢就不會重複產生了,所以一個交易的查詢結果每次都是一樣的

解決幻讀問題

  • 快照讀:透過MVCC來控制的,不用加鎖。依照MVCC中規定的「文法」進行增刪改查等操作,以避免幻讀。

  • 目前讀取:透過next-key鎖定(行鎖 gap鎖定)來解決問題的。

RC、RR層級下的InnoDB快照讀取區別

  • #在RR層級下的某個交易的對某筆記錄的第一次快照讀取會創建一個快照及Read View, 將當前系統活躍的其他事務記錄起來,此後在調用快照讀的時候,還是使用的是同一個Read View,所以只要當前事務在其他事務提交更新之前使用過快照讀,那麼之後的快照讀使用的都是同一個Read View,所以對之後的修改不可見;

  • 即RR等級下,快照讀產生Read View時,Read View會記錄此時所有其他活動事務的快照,這些事務的修改對於目前事務都是不可見的。而早於Read View創建的事務所所做的修改均是可見

  • 而在RC級別下的,事務中,每次快照讀取都會新生成一個快照和Read View,這就是我們在RC層級下的事務中可以看到別的事務提交的更新的原因

#總結

從以上的描述我們可以看出來,所謂的MVCC指的就是在使用READ COMMITTDREPEATABLE READ這兩個隔離等級的交易在執行普通的SEELCT操作時存取記錄的版本鏈的過程,這樣子可以使不同事務的讀-寫寫-讀操作並發執行,從而提升系統性能

想了解更多程式設計學習,請關注php培訓欄位!

#

以上是全網最全的一篇資料庫MVCC詳解,不全我負責的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.im
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板