讀了 @SnailMann大佬【MySQL筆記】正確的理解MySQL的MVCC及實現原理 收益頗豐,非常感謝!
但對其中如何判斷事務是否可見性還是不太理解,於是作了本文,在原部落格基礎上,舉例畫圖論證、理解了Read View
的可見性判斷。
引用 @SnailMann大佬【MySQL筆記】正確的理解MySQL的MVCC及實現原理 的欄位說明。
隱式欄位
每行記錄除了我們自定義的欄位外,還有資料庫隱式定義的 DB_TRX_ID
, DB_ROLL_PTR
, DB_ROW_ID
等欄位
DB_TRX_ID
6 byte,最近修改(修改/插入)事務 ID:記錄建立這條記錄/最後一次修改該記錄的事務 IDDB_ROLL_PTR
7 byte,回滾指標,指向這條記錄的上一個版本(儲存於 rollback segment 裡)DB_ROW_ID
6 byte,隱含的自增 ID(隱藏主鍵),如果資料表沒有主鍵,InnoDB 會自動以DB_ROW_ID產生一個聚簇索引
Read View
的三個全域性屬性
trx_list
(名稱我隨意取的):一個數值列表,用於維護Read View
生成時刻系統 正活躍的事務 ID 列表
up_limit_id
:是trx_list
列表中事務 ID 最小的 ID
low_limit_id
:Read View
生成時刻系統尚未分配的下一個事務 ID ,也就是 目前已出現過的事務 ID 的最大值 + 1
為什麼是low_limit
? 因為它也是系統此刻可分配的事務 ID 的最小值
可見性判斷邏輯
-
DB_TRX_ID < up_limit_id
, 當前行事務id比活躍的最小事務id還小時,說明了兩件事,當前行事務對該記錄的修改已經提交,因為當前事務id比活躍的最小事務id還小,不在活躍的事務之中,也就意味著該事務已經提交或回滾,這時因為已經成功修改,那麼應該就是提交成功了。
也就是在生成Read View
之前,事務已經提交, -
接下來判斷
DB_TRX_ID >= low_limit_id
, 修改該行的事務id大於了Read View
裡系統待分配的下一個事務id,說明修改該行的事務是生成該Read View
之後出現的事務,因為Read View
系統待分配的下一個事務id被用了,才會出現比該事務id大的事務。這時,也應該是不可見的,一個事務怎麼可以看到後面新來事務做的修改了。 -
判斷
DB_TRX_ID
是否在活躍事務之中,trx_list.contains (DB_TRX_ID)
,如果在活躍事務之中,說明該修改是其他事務未提交的修改,應該是不可見的,如果可見就是髒讀了,如果不在活躍事務之中,說明在生成Read View
之前,該事務的修改就已提交,與第一個判斷邏輯類似,事務2是可以查到這條記錄的。
針對上面三種情況,下面舉例說明:
原記錄:amount = 100
事務1 | 事務2 | 事務3 | 事務4 |
---|---|---|---|
開啟事務 | 開啟事務 | 開啟事務 | |
update amount = 200 |
|||
update amount = 300 |
提交事務 | ||
①select amount; 開始快照讀,生成Read View |
|||
提交事務 | 開啟事務 | ||
②select amount; |
|||
update amount = 400; |
|||
提交事務 | |||
③select amount; |
請問三次select amount;
快照讀到的值分別是多少,為什麼?
畫一張圖,把undo表裡存的記錄版本鏈及當前記錄畫出來。
①
1>如圖,當前行 DB_TRX_ID(1) == up_limit_id(1)
,說明本次修改該記錄的事務正在進行中,也就是事務1還未結束,事務2就應該對事務1這次修改不可見,可見就是髒讀了。
2>當前記錄不可見,再根據回滾指標追蹤到上個版本記錄,如圖undo日誌內 金額為200的行,此時再通過Read View進行可見性判斷。
第一種情況:當前行 DB_TRX_ID(3) > up_limit_id(1)
,不確定;
第二種情況:DB_TRX_ID(3) < low_limit_id(4)
,也不確定;
第三種情況:DB_TRX_ID(3)
不在trx_list
中,不是活躍的事務,說明事務3在事務2生成Read View
之前就已經提交,那麼是可見的。
所以讀取的金額為200。
②
事務1提交事務,不過undo表與當前行資料無變化,對事務1的Read View
的資料也不會變化,因為RR模式下,Read View
只會在第一次快照讀時生成,後面幾次快照讀不會生成新的 Read View
,也不會改動之前Read View的值。
當前行資料與Read View
都無變化,那麼可見性判斷也同①一致,讀取到的金額為200。
③
第一種情況:當前行 DB_TRX_ID(4) > up_limit_id(1)
,不確定;
第二種情況:DB_TRX_ID(4) > low_limit_id(1)
,說明當前行是被生成Read View
之後出現的事務修改的,這種未來的資料肯定是不可見的。
再接著追溯,就與①中追溯的過程相差不大了,最終讀取的金額也是為200。
總結
這裡舉例論證了可見性判斷的合理性,總結來說,可見性的三個判斷約束了一件事,只有在本事務生成Read View
之前就已經提交的事務的修改才可以被看見,其他的無論是正在進行的事務的修改還是之後再提交的事務的修改都不可見。