天天看點

mysql中的mvcc解讀

原文連結 : https://www.toutiao.com/i6674177996649136653

對于MVCC想必大家也看到了不少源碼層的解讀,最大特點就是分析的是比較深入了,但是卻不大好了解,最後有種不明覺厲的感覺,以至于在面試中經常翻船。

我們換個角度來解讀一下, 在表設計中,我們有一種政策,那就是盡可能保留資料變化的

曆史,比如在資料發生變化時我們不會直接删除資料,而是把它轉換為兩類操作。

比如修改一個賬戶的餘額,這是敏感資訊,屬于狀态型資料,在更新時需要保留完整的資料

變化曆史,那麼把餘額從100變化為200的過程,會轉化為1條 update 語句,1條 insert 語句。

如下的操作是我們預期的結果:

可以把這個過程改造為:

有的同學說,這個和 MVCC 有什麼關系呢,其實 MVCC 的實作原理也是類似的方式,我們就以

這種方式作為例子來解釋,在這種情況下,第1行 update 語句對應的資料可以了解為是之前

的資料鏡像,而第2行則是資料處理後的結果。

如果存在大量的并發讀寫,我們可以把讀的壓力分擔出來,即資料的查詢可以指向鏡像,而

資料的修改指向目前的變化資料,這樣兩者是一個互補的關系。

這種情況類似下面的方式,比如 T1,T2,T3三個順序時間裡發生了三次請求,分别是一次寫

請求和兩次讀請求.

那麼在 MySQL 中會先在 T1時間生成一個快照,比如資料辨別是90,然後在這個基礎上進行

資料修改,資料辨別為100,但是事務未送出。

在 T1寫資料的事務内,T2時間的讀請求會讀取 T1時間生成的快照資料,讀取的資料辨別

依舊是 90,T3時間的讀請求也是類似。是以MVCC本身還是比較接地氣的,隻是我們了解的方式有些高大上,消化不了了。我們小結一下:

1.表設計中資料生命周期的管理是一種體系化的管理方式,原理和思路是通用的。2.資料生命周期管理有兩個重要的辨別,一個是辨別資料變化的,一個是辨別資料可用狀态的。

明白了這些,了解 InnoDB 的 MVCC 就很簡單了,我們使用類似的思路來做下解讀,假設在每

行記錄後面儲存兩個隐藏的列來實作的,這兩個列,分别儲存了這個行的建立時間,一個保

存的是行的删除時間。這裡存儲的是系統版本号,會自動遞增,我們按照 DML 的幾個次元進

行闡述。

1)、首先是 Insert 操作, 事務 id 假設是1

id name create version delete version

1 test 1

2)、Update 操作,會先把目前記錄辨別為已删除,然後新增一列資料,寫入相應的版本号,

在這裡就是2,和上一條的 delete_version 是一緻的,比如把字段 name 修改為 new_test

1 test 1 2

1 new_test 2

3)delete 操作,就是把目前記錄辨別為已删除

1 new_value 2 3

此外需要考慮的是上面的實作方式中,如果事務發生復原該如何處理,這個是我們需要重點

考慮的,也是對資料周期管理流程的一個補充,這裡我們就要引出 InnoDB 層的實作 undo.

我們來設想一個問題,原有的鏡像資料在表中存放顯然是難以維護的,而且從存儲上也是一

筆不小的開銷,是以從成本效益考慮,這部分的内容應該是獨立存放的,這個存放的地方就是

undo 日志裡面,一旦出現了事務復原,我們可以把已有的資料狀态通過逆向應用保證事務

的 ACID 特性。

要實作這個細粒度的操作,在 InnoDB 的設計中,實際上所有行資料會增加三個内部屬性列:

(1)DB_TRX_ID,6位元組,記錄每一行最近一次修改它的事務 ID;

(2)DB_ROLL_PTR,7位元組,記錄指向復原段 undo 日志的指針;

(3)DELETE BIT,辨別該記錄是否被删除,不是真正的删除資料;

把這三個列組合起來,就可以标記資料的周期性,并定位到相應的事務,在需要的時候進行

復原。

比如一張表 test (id,name)主鍵為 id 列

 insert 的資料在 redo 中順序記錄 insert 操作,同時生成 undo 記錄,為逆操作 delete

 delete 的資料在 redo 中順序記錄 delete 操作,同時生成 undo 記錄,為逆操作 insert

 update 的資料在 redo 中順序記錄 update 操作,同時生成 undo 記錄,為逆操作 update,

原來是從 id=1 變成 id=3,則逆操作為 id =3,變成 id=1

對于 InnoDB 來說,無論是更新,删除,都隻是設定行記錄上的 deleted version 标記位,

而不是真正的删除記錄,後續這些記錄的清理,是通過 Purge 背景程序實作的。

此外,需要說明的是隻有在隔離級别 read-committed 和 repeatable-read 才能使用 MVCC,

read-uncommited 由于是讀到未送出的,是以不存在版本的問題,而 serializable 則會對

所有讀取的行加鎖。