一、開場白
MVCC(Multi-Version Concurrency Control)即多版本并發控制,通俗講就是“通過多個版本的記錄來實作并發控制”。并發控制的定義所述者衆多,此不贅述。
實作并發控制的方法有:鎖,MVCC。這篇文化講的就是MVCC
二、MVCC存在的意義
剛才說了,實作并發控制方法有鎖和MVCC。衆所周知,如果存在兩個幹同樣事情的東西,我們都要分析對比其優劣。那麼MVCC和鎖有何優劣對比呢?再說直白一些,既然鎖可以實作并發控制,為什麼還需要MVCC呢?
因為純鎖的方式隻實作了讀-讀相容,而讀-寫,寫-讀,寫-寫都是沖突的,這就會産生較多的鎖阻塞,并發效率低。 而我們親愛的MVCC,它實作了讀-寫,寫-讀的相容,大大減少了鎖阻塞,提高了并發效率!
三、MVCC如何實作讀和寫相容
1、四個隐藏字段
列名 | 位元組數 | 含義 | 作用 |
DB_TRX_ID | 6 | 記錄的版本号 | Insert或Update行的最後一個事務的事務ID。(Delete也視為更新,并需要将其标記為已删除) |
DB_ROLL_PTR | 7 | 復原指針 | 指向最近的曆史資料 |
DB_ROW_ID | 6 | 行隐藏主鍵 | 當表沒有設定主鍵時,系統自動為其設定的隐藏主鍵 |
還有一個删除标志,當該行被執行Delete時,将此标志設為已删除狀态,但是記錄依然存在,要等事務送出後才會删除。
2、undo log
undo log即復原日志,屬于InnoDB的事務日志。雖然叫復原日志,但是其作用卻不僅僅限于復原!
undo log記錄了曆史資料,這些曆史資料的作用是:
(1)實作復原,以保證事務的原子性
(2)實作MVCC,以實作事務的隔離性
本文隻說第二點,即undo log在MVCC中發揮的作用
3、版本鍊
每開啟一個事務,事務會用目前的系統版本号作為自己的事務ID,并且系統版本号都會增加1。舉個例子:
想象這樣一個場景,很多人進入一個會場,會場入口有一個發号員,入場者根據自己拿到的号去對号入座。發号員的發号規則是:按照遞增順序發号!即來一個人,發1号,再來一個人,發2号,又來一個人,發3号...以此類推。這樣的結果就是,每個人拿到的号都不一樣,各自做自己的位置。(了解了這個例子隻後,就忘掉它,因為這個例子對後面的了解沒用,甚至會誤導你)
每次對記錄進行update操作時,都會把舊的記錄标記為已删除,寫入undo日志中,并且把新記錄的復原指針指向這條舊記錄,新紀錄DB_TRX_ID字段設為目前事務ID。每次修改都這樣操作,形成的版本鍊是這樣的。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLyUFVPlXVE1UeRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzAjMzUzM0YTMyEjMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
圖檔來自 這篇文章
4、MVCC中的Insert和Delete操作
上面說了MVCC中的Update操作的做法,那Insert和Delete操作是如何做的呢?
Insert操作會産生一條新的記錄,其DATA_TRX_ID字段為目前事務的ID,其DATA_ROLL_PTR字段為空。
Delete操作會将目前行标記為已删除,DATA_TRX_ID字段為目前事務的ID。
5、MVCC實作可重複讀
MVCC通過快照讀實作可重複讀,即在事務中第一條Select語句開始時生成一個ReadView,之後該事務中所有的簡單Select(沒有for update和lock in share model)都會在此ReadView上進行,并且對于傳回的記錄,必須滿足:
(1)如果沒有被标記為删除,其DATA_TRX_ID字段值小于或等于目前事務ID的記錄。當滿足此條件的記錄有多個時,選DATA_TRX_ID字段值最大的(即最近的曆史值)
(2)如果已經标記為删除,其DATA_TRX_ID字段值大于目前事務ID的記錄。
6、讀-寫相容
有了上述這些控制,在進行資料庫讀寫時,讀操作不用阻塞寫操作,寫操作不用阻塞讀操作,而且不會産生并發問題。這裡的寫操作包括:Insert,Update,Delete
如有錯誤之處,感謝指出
四、遺留問題
1、MVCC實作方式(悲觀鎖,樂觀鎖)
2、undo log的具體寫入和儲存流程