什么是 MVCC

文 |?豆豆
來源:Python 技術「ID: pythonall」

上一篇文章我們說到數(shù)據(jù)庫的四種事務隔離級別,可以通過加鎖的方式來實現(xiàn),只是效率太低,事實上,MySQL 是通過 MVCC(多版本并發(fā)控制)來實現(xiàn)的。
具體原理有一點點復雜,需要你用點心才能看懂,今天我們就以「可重復讀隔離級別」為例來詳細說明其具體原理。
假設數(shù)據(jù)庫有如下記錄。

我們都知道 InnoDB 引擎下,每一個事務都有一個事務 ID,叫做 transaction id,是在事務開始時系統(tǒng)自動分配的,且該 id 是遞增的。同時這個 id 也會記錄在數(shù)據(jù)行上面。言外之意就是數(shù)據(jù)庫表的每行記錄是有多個版本的,用事務 ID 來標示這行記錄是屬于哪個事務操作的。既然是有多個版本,那怎么拿到舊版本的數(shù)據(jù)記錄呢,答案就是在添加一個指針,指向前一個事務 ID 標識的記錄。

而這個指針就是我們通常訴所說的 undo log,事實上,舊版本的行記錄都不是物理存在的,而是通過 undo log 實時計算出來的。
一致性視圖
好了,了解了行記錄事務 ID 和回滾指針的概念之后,我們來看看一致性視圖。
當在一個事務 A 中查詢數(shù)據(jù)時,只需要確定行記錄的數(shù)據(jù)版本是否在事務 A 啟動之前生成的即可,如若是,則可見;否則不可見,則需通過回滾指針找到上一個版本來繼續(xù)判斷屬否可見,
因此,就需要在事務啟動時,為該事務構(gòu)造一個事務數(shù)組,用來保存該事務啟動瞬間,系統(tǒng)中正在活躍的所有事務 ID,也就是啟動了但還未提交的事務 ID。
數(shù)組中的最小值我們簡記為低位,系統(tǒng)中的最大事務 ID 記錄為高位,正是這個高位和視圖數(shù)組組成了我們說的「一致性視圖」。
因此,對于任何一個事務 A 來說,任何數(shù)據(jù)版本的可見行都可以通過一致性視圖來得到。
如果數(shù)據(jù)版本小于低位,說明是已經(jīng)提交的記錄,則可見。 如果大于高位,說明是由未來的事務生成的,則不可見。 如果在高位和低位之間: 若在數(shù)組中,說民該事務還未提交,則不可見, 如果不在數(shù)組中,說明該事務是已經(jīng)提交了的,則可見。
實戰(zhàn)分析
我們針對上圖的數(shù)據(jù)記錄,假設有以下三個事務,我們來具體分析下,其一致性視圖是怎么樣的。

假設(1,1)這行記錄的事務 ID 是 90;事務 A 開啟前,系統(tǒng)里面只有一個活躍的事務,ID = 99;事務 A、B、C 的版本號分別是 100、101、102。
現(xiàn)在,事務 A、B、C 的視圖數(shù)組和高位分別是[99,100]/101,[99,100,101]/102,[99,100,101,102]/103。

你要知道,讀取數(shù)據(jù)是從當前版本往前讀的,對于事務 A 來說:
當讀取到 (1,3) 的時候,事務 ID = 101,高于高位,不可見。 往前讀取歷史版本(1,2),事務 ID = 102,高于高位,不可見。 繼續(xù)往前讀取歷史版本(1,1),事務 ID = 90,低于低位,可見。
這樣,我們就通過一致性視圖和事務 ID 找到了可見的數(shù)據(jù)版本,不論事務 A 是什么時候查詢的,看到的記錄都是一致的。
讀提交
上面我們分析了可重復讀隔離級別下的一致性視圖,那么在讀取提交的隔離級別下,又是怎么樣的呢?
事實上,他們最主要的區(qū)別就是一致性視圖的創(chuàng)建時間不一樣,對于可重復讀隔離級別,一致性視圖是在事務開啟時刻生成的,之后在該事務中的查詢都共用這個一個視圖。而對于讀提交隔離級別,每一個語句執(zhí)行前都會生成一個新的視圖。
因此,事務 A、B、C 的視圖數(shù)組和高位分別是[99,100,101]/103,[99,101]/103,[99,102]/103。
對于事務 A 來說:
當讀取到 (1,3) 的時候,事務 ID = 101,在數(shù)組中,不可見。 往前讀取歷史版本(1,2),事務 ID = 102,不在數(shù)組中,可見。
總結(jié)
今天我們學習了 MVCC(多版本并發(fā)控制)的底層實現(xiàn)方式,雖然過程略顯復雜,但這保證了在不加鎖的情況下,保證了可重復讀,和讀提交。讀寫不互相干擾,能極大的提高并發(fā)能力。
別看上面的分析步驟較復雜,事實上我們只需要記住以下兩條規(guī)則即可。在可重復讀隔離級別下,查詢只能看到在事務啟動前已經(jīng)提交的數(shù)據(jù)。在讀提交隔離級別下,查詢只能看到在語句啟動前已經(jīng)提交的數(shù)據(jù)。
參考
極客時間 「MySQL實戰(zhàn)45講 08節(jié)」。
PS:公號內(nèi)回復「Python」即可進入Python 新手學習交流群,一起 100 天計劃!
老規(guī)矩,兄弟們還記得么,右下角的 “在看” 點一下,如果感覺文章內(nèi)容不錯的話,記得分享朋友圈讓更多的人知道!


【代碼獲取方式】
