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
id name create version delete version
1 test 1 2
1 new_test 2
3)delete 操作,就是把當前記錄標識為已刪除
id name create version delete version
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 則會對
所有讀取的行加鎖。

浙公網安備 33010602011771號