MVCC詳解,深入淺出簡單易懂

dewxin發表於2024-09-01

轉載自 https://blog.csdn.net/lans_g/article/details/124232192

配合 https://www.bilibili.com/video/BV1Hr421p7EK 影片觀看更佳。

一、什麼是MVCC

mvcc,也就是多版本併發控制,是為了在讀取資料時不加鎖來提高讀取效率和併發性的一種手段。

資料庫併發有以下幾種場景:

  • 讀-讀:不存在任何問題。
  • 讀-寫:有執行緒安全問題,可能出現髒讀、幻讀、不可重複讀。
  • 寫-寫:有執行緒安全問題,可能存在更新丟失等。

mvcc解決的就是讀寫時的執行緒安全問題,執行緒不用去爭搶讀寫鎖

mvcc所提到的讀是快照讀,也就是普通的select語句。快照讀在讀寫時不用加鎖,不過可能會讀到歷史資料。

還有一種讀取資料的方式是當前讀,是一種悲觀鎖的操作。它會對當前讀取的資料進行加鎖,所以讀到的資料都是最新的。主要包括以下幾種操作:

  • select lock in share mode(共享鎖)
  • select for update(排他鎖)
  • update(排他鎖)
  • insert(排他鎖)
  • delete(排他鎖)

二、MVCC的實現

1.回顧事務的特性

  • 原子性:透過undolog實現。
  • 永續性:透過redolog實現。
  • 隔離性:透過加鎖(當前讀)&MVCC(快照讀)實現。
  • 一致性:透過undolog、redolog、隔離性共同實現。

2.回顧事務的隔離級別

  • 讀未提交:允許讀取尚未提交的資料變更。可能會導致髒讀、幻讀或不可重複讀
  • 讀已提交:允許讀取已經提交的資料。可能會導致幻讀和不可重複讀
  • 可重複讀:對同一欄位的多次讀取結果都是一致的,除非資料是被本身事務自己所修改。可能會導致幻讀
  • 可序列化:最高隔離級別。

在讀已提交和可重複讀隔離級別下的快照讀,都是基於MVCC實現的!

3.mvcc實現原理

​ mvcc的實現,基於undolog版本鏈readview

MVCC詳解,深入淺出簡單易懂

在mysql儲存的資料中,除了我們顯式定義的欄位,mysql會隱含的幫我們定義幾個欄位。

  • trx_id:事務id,每進行一次事務操作,就會自增1。

  • roll_pointer:回滾指標,用於找到上一個版本的資料,結合undolog進行回滾。

什麼是readview呢?

當我們用select讀取資料時,這一時刻的資料會有很多個版本(例如上圖有四個版本),但我們並不知道讀取哪個版本,這時就靠readview來對我們進行讀取版本的限制,透過readview我們才知道自己能夠讀取哪個版本

在一個readview快照中主要包括以下這些欄位:

image-20220417154721232

對readview中的引數做一些解釋

m_ids:活躍的事務就是指還沒有commit的事務。

max_trx_id:例如m_ids中的事務id為(1,2,3),那麼下一個應該分配的事務id就是4,max_trx_id就是4。

creator_trx_id:執行select讀這個操作的事務的id。

readview如何判斷版本鏈中的哪個版本可用呢?(重點!)

image-20220417160003011

從上到下分別為(1)(2)(3)(4),依次進行解釋

trx_id表示要讀取的事務id

(1)如果要讀取的事務id等於進行讀操作的事務id,說明是我讀取我自己建立的記錄,那麼為什麼不可以呢。

(2)如果要讀取的事務id小於最小的活躍事務id,說明要讀取的事務已經提交,那麼可以讀取。

(3)max_trx_id表示生成readview時,分配給下一個事務的id,如果要讀取的事務id大於max_trx_id,說明該id已經不在該readview版本鏈中了,故無法訪問。

(4)m_ids中儲存的是活躍事務的id,如果要讀取的事務id不在活躍列表,那麼就可以讀取,反之不行。

4.mvcc如何實現RC和RR的隔離級別

(1)RC的隔離級別下,每個快照讀都會生成並獲取最新的readview

(2)RR的隔離級別下,只有在同一個事務第一個快照讀才會建立readview,之後的每次快照讀都使用的同一個readview,所以每次的查詢結果都是一樣的

5.幻讀問題
  • 快照讀:透過mvcc,RR的隔離級別解決了幻讀問題,因為每次使用的都是同一個readview。
  • 當前讀:透過next-key鎖(行鎖+gap鎖),RR隔離級別並不能解決幻讀問題

相關文章