天天看點

從一個問題出發,搞懂MySQL MVCC、readview、快照讀和undo log是什麼鬼序詳細說明總結

摸魚的時候看到某技術群裡有一個問題和下面的回複,在讨論幻讀與MySQL的快照讀

問:大佬們為什麼會産生幻讀,是不是因為重新生成了readview?

首先明确一個定義,幻讀是指多次查詢時,查詢到的資料數量出現了變化。

例如:第一次查詢到了10行,第二次查詢到了11行。

這種情況在MySQL的Read Committed(RC)隔離級别中會出現,在标準資料庫定義中,Repeatable Read(RR)隔離級别中也會出現,MySQL通過快照讀的方式避免了幻讀。

和問題中所說的是不是因為readview導緻的幻讀,其實恰恰相反readview是MySQL實作快照讀所生成的資料結構,但并不是說快照讀就一定不會出現幻讀,下面再細講

A群友答:幻讀隻有在目前讀才會出現,目前讀讀的是最新值,不是讀的視圖。

B群友答:讀已送出的隔離級别下快照讀也會造成現幻讀

C群友答:要搞清楚幻讀的概念, 僅在可重複讀隔離級别的目前讀

D群友答:RC RR都會出現幻讀,需要靠間隙鎖解決,光靠mvcc是解決不了幻讀問題

其實我回複的是群友D的答案,其他隻是在扣概念可以忽略,僅作為上下文閱讀,但D的回答是明顯有誤的

詳細說明

前面也提到MySQL是通過快照讀避免幻讀的,MySQL通過undo log,記錄每次修改的復原操作,可以了解成一條連結清單。

例如:

  • 原值A=1
  • 事務1,set A=2
  • 事務2,set A = 3
  • 事務3,set A= 4

那麼連結清單上就有4個節點,1->2->3->4,并且節點上也會記錄做這次修改的事務id,通過事務id和和undo log,我們就可以推算出可見的資料版本了。

從一個問題出發,搞懂MySQL MVCC、readview、快照讀和undo log是什麼鬼序詳細說明總結

可見性規則

在事務開始的時候,會記錄目前未送出的所有事務id,這個就是提問中所說的readview。

判斷邏輯

從最新的版本開始判斷,邏輯如下:

  • 目前版本如果比記錄的所有事務id都大,即在目前事務開始的時候,該事務并未啟動,是以一定不可見的。
  • 目前版本如果比記錄的所有事務id都小,即在目前事務開始的時候,該事務已送出,是以可見
  • 目前版本在readview中,即在目前事務開始的時候,該事務未送出,不可見
  • 目前版本不在readview中,即在目前事務考試的時候,該事務已送出,可見
  • 更新邏輯,因為更新不可能在快照上做更新,是以更新的時候是讀取最新的資料上再做修改,且因為修改之後的undo log會記錄自己的事務id,是以自己再次查詢也是可見的

回到問題

RC和RR的差别在于,RC每次查詢都會生成新的readview,RR隻有事務開啟的時候會生成readview,是以在RC隔離級别,是會産生幻讀的,而在RR隔離級别,因為readview的存在,并不需要依賴鎖機制去保障資料的可見性。

總結

對應标題總結一下:

  • MVCC:Multiversion concurrency control(多版本并發控制),是MySQL保障資料可見性的手段
  • 快照讀:是MVCC下的具體讀取操作
  • undo log:是快照讀真正讀取的快照資料
  • readview:是判斷undo log可見性規則的依賴資料

嗯,沒錯,擱這套娃呢