MVCC併發版本控制 本文大部分來自《MySQL是怎樣運行的》,這裡只是簡單總結,用於各位回憶和複習。 版本鏈 對於使用 InnoDB 存儲引擎的表來說,它的聚簇索引記錄中都包含兩個必要的隱藏列(不知道的快去看《MySQL是怎樣運行的》) trx_id :每次一個事務對某條聚簇索引記錄進行改動時,都 ...
版本鏈
對於使用 InnoDB 存儲引擎的表來說,它的聚簇索引記錄中都包含兩個必要的隱藏列(不知道的快去看《MySQL是怎樣運行的》)
-
trx_id :每次一個事務對某條聚簇索引記錄進行改動時,都會把該事務的 事務id 賦值給 trx_id 隱藏列。
-
roll_pointer :每次對某條聚簇索引記錄進行改動時,都會把舊的版本寫入到 undo日誌中,然後這個隱藏列就相當於一個指針,可以通過它來找到該記錄修改前的信息。
假設現在有兩個事務id 分別為 100 、 200 的事務對這條記錄進行 UPDATE 操作,操作流程如下:
每次對記錄進行改動,都會記錄一條undo日誌(不懂的可以理解為一個記錄著過去的操作的日誌) ,每條 undo日誌 也都有一個 roll_pointer 屬性( INSERT 操作對應的 undo日誌 沒有該屬性,因為該記錄並沒有更早的版本),可以將這些 undo日誌 都連起來,串成一個鏈表,所以現在的情況就像下圖一樣:
對該記錄每次更新後,都會將舊值放到一條 undo日誌 中,就算是該記錄的一個舊版本,隨著更新次數的增多, 所有的版本都會被 roll_pointer 屬性連接成一個鏈表,我們把這個鏈表稱之為 版本鏈 ,版本鏈的頭節點就是當 前記錄最新的值。另外,每個版本中還包含生成該版本時對應的 事務id ,這個信息很重要,我們稍後就會用到。
ReadView
我們來引出一下readview是什麼東西:
對於使用 READ UNCOMMITTED 隔離級別的事務來說,由於可以讀到未提交事務修改過的記錄,所以直接讀取記錄的最新版本就好了
對於使用 SERIALIZABLE 隔離級別的事務來說,則是使用加鎖的方式來問記錄
但是!!!對於使用 READ COMMITTED 和 REPEATABLE READ 隔離級別的事務來說
都必須保證讀到已經提交了的事務修改過的記錄,也就是說假如另一個事務已經修改了記錄但是尚未提交, 是不能直接讀取最新版本的記錄的,核心問題就是:需要判斷一下版本鏈中的哪個版本是當前事務可見的。為此,設計 InnoDB 的大叔提出了一個 ReadView 的概念,這個 ReadView 中主要包含4個比較重要的內容:
-
m_ids :表示在生成 ReadView 時當前系統中活躍的讀寫事務的事務id 列表。
-
min_trx_id :表示在生成 ReadView 時當前系統中活躍的讀寫事務中最小的事務id ,也就是 m_ids 中的最小值。
-
max_trx_id :表示生成 ReadView 時系統中應該分配給下一個事務的 id 值。
-
小貼士: 註意max_trx_id並不是m_ids中的最大值,事務id是遞增分配的。比方說現在有id為1,2,3這三 個事務,之後id為3的事務提交了。那麼一個新的讀事務在生成ReadView時,m_ids就包括1和2,mi n_trx_id的值就是1,max_trx_id的值就是4。
-
-
creator_trx_id :表示生成該 ReadView 的事務的事務id 。
-
小貼士: 我們前邊說過,只有在對錶中的記錄做改動時(執行INSERT、DELETE、UPDATE這些語句時)才會 為事務分配事務id,否則在一個只讀事務中的事務id值都預設為0。
-
有了 ReadView ,這樣在訪問某條記錄時,只需要按照下邊4個步驟判斷記錄的某個版本是否可見:註意,與被訪問版本對比的東西都是指(當前最新的ReadView)
-
如果(被訪問版本的) trx_id = creator_trx_id ,意味著當前事務在訪問它自己修改過的記錄,所以該版本可以被當前事務訪問。
-
如果(被訪問版本的) trx_id < min_trx_id ,表明生成該版本的事務在當前事務生成 ReadView 前已經提交,所以該版本可以被當前事務訪問。
-
如果(被訪問版本的) trx_id > max_trx_id ,表明生成該版本的事務在當前事務生 成 ReadView 後才開啟,所以該版本不可以被當前事務訪問。
-
如果(被訪問版本的)min_trx_id < trx_id < max_trx_id ,那就需要判斷一下 trx_id 屬性值是不是在 m_ids 活躍事務列表中。
-
如果在,說明創建 ReadView 時生成該版本的事務還是活躍的,還沒提交,該版本不可以被訪問。
-
如果不在,說明創建 ReadView 時生成該版本的事務已經被提交,該版本可以被訪問。
-
ReadView生成時機
讀已提交 和 可重覆讀 隔離級別最大的區別就是它們生成ReadView的時機不同:
-
READ COMMITTED —— 每次讀取數據前都生成一個ReadView
-
每次select操作都生成一個ReadView,所以每一次查詢都在用一個最新生成的ReadView進行上述4步走,所以查出來的應該是最新提交的事務中的最後一個操作(因為一個事務可能有多個增改操作)
-
-
REPEATABLE READ —— 在第一次讀取數據時生成一個ReadView
-
意味著你在第一次執行select語句之後,不管增刪改了多少次,永遠拿第一次select生成的ReadView來進行剛剛說的4個步驟判斷,所以一直查詢的是第一次select語句所查出來的東西。
-