MySQL MVCC機制詳解

MySQL MVCC機制詳解

MVCC分析

  MySQL默認的事務隔離級別是可重複讀,在事務開始時獲得一致性視圖,但更新數據時需要拿到行鎖,如果與其他事務出現衝突需要進行等待,等待後獲得鎖再進行更新操作時讀到的值是最新值還是創建一致性視圖時的值呢?下面我們通過一個實驗來驗證。
  下面爲一張表的創建語句:

mysql> CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `k` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

  然後我們分別開啓三個事務

事務A 事務B 事務C
start transaction with consistent snapshot
start transaction with consistent snapshot
update t set k = k+1 where id = 1;
update t set k = k+1 where id = 1;select k from t where id = 1;
select k from t where id = 1;commit
commit;

   注意,事務啓動並非是start transition,而是執行到第一個InnoDB操作語句,上面的實驗結果爲:事務A查詢到的值爲1,事務B查詢到的值爲3。下面將詳細解釋一下原因。
   首先MySQL的事務隔離通過MVCC來實現,每個事務在啓動的時候會得到一個嚴格遞增的事務ID: transaction id,而在保存數據時,每行數據都有多個版本,每次更新時並不覆蓋原始數據而是生成多個版本,版本之間通過row trx_id進行區別,row trx_id的值就是更新的事務的transaction id(並不是真的保存那麼多行,而是用的時候通過undo log恢復)
   然後,在每個事務啓動時,會用一個數組來記錄下這個瞬間所有活躍事務的ID(啓但沒提交),將這組ID中的最小值記爲低水位,當前系統裏面已經創建過的事務ID的最大值加1記爲高水位,然後將所有的row trx_id分成三類:
在這裏插入圖片描述

  1. 如果落在綠色部分,表示這個版本是已提交的事務或者是當前事務自己生成的,這個數據是可見的;
  2. 如果落在紅色部分,表示這個版本是由將來啓動的事務生成的,是肯定不可見的;
  3. 如果落在黃色部分,那就包括兩種情況
    a. 若 row trx_id在數組中,表示這個版本是由還沒提交的事務生成的,不可見;
    b. 若 row trx_id不在數組中,表示這個版本是已經提交了的事務生成的,可見

下面分析一下上面的ABC事務執行的問題
   不妨設事務A,B,C的ID分別爲100,101,102,則有:
在這裏插入圖片描述
   如圖所示,對於事務A來說,它的transition id爲100,所以只能看到90時候的數據,所以k=1,而對於事務C來說,也毫無疑問應該看到k=2;唯一比較奇怪的是對於事務B,由於事務B是一個更新操作,對於所有的更新數據都是先讀後寫的,而這個讀,只能讀當前的值,稱爲“當前讀”(current read)。因此,在更新的時候,當前讀拿到的數據是(1,2),更新後生成了新版本的數據(1,3),這個新版本的row trx_id是101,當之後執行事務B的查詢語句的時候,一看自己的版本號是101,最新數據的版本號也是101,是自己的更新,可以直接使用,所以查詢得到的k的值是3。
   對於當前讀來說,除了update,也可以通過select + lock in share mode 或 for update來實現,注意當前讀是需要加鎖的,如果有事務正在修改這一行,那麼當前事務需要等待。

參考文獻

MySQL實戰45講

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章