1. 程式人生 > >mysql多版本控制-MVCC

mysql多版本控制-MVCC

一、定義

多版本控制: 指的是一種提高併發的技術。最早的資料庫系統,只有讀讀之間可以併發,讀寫,寫讀,寫寫都要阻塞。引入多版本之後,只有寫寫之間相互阻塞,其他三種操作都可以並行,這樣大幅度提高了InnoDB的併發度。在內部實現中,與Postgres在資料行上實現多版本不同,InnoDB是在undolog中實現的,通過undolog可以找回資料的歷史版本。找回的資料歷史版本可以提供給使用者讀(按照隔離級別的定義,有些讀請求只能看到比較老的資料版本),也可以在回滾的時候覆蓋資料頁上的資料。在InnoDB內部中,會記錄一個全域性的活躍讀寫事務陣列,其主要用來判斷事務的可見性。

二、資料庫多版本讀場景

session 1 session 2
select a from test; return a = 10  
start transaction;  
update test set a = 20;  
  start transaction;
  select a from test; return ?
commit;  
  select a from test; return ?

我們看下上面這個資料庫日常操作的例子。

  • session 1修改了一條記錄,沒有提交;與此同時,session 2 來查詢這條記錄,這時候返回記錄應該是多少呢?
  • session 1 提交之後 session 2 查詢出來的又應該是多少呢?

由於MySQL支援多種隔離級別,這個問題是需要看session2的事務隔離級別的,情況如下:

  • 隔離級別為 READ-UNCOMMITTED 情況下: 
    session 1 commit前後 session 2 去檢視都會看到的是修改後的結果 a = 20
  • 隔離級別為 READ-COMMITTED 情況下: 
    session 1 commit 前檢視到的還是 a =10 , commit之後看到的是 a = 20
  • 隔離級別為 REPEATABLE-READ, SERIALIZABLE 情況下: 
    session 1 commit前後 session 2 去檢視都會看到的是修改後的結果 a = 10

其實不管隔離級別,我們也拋開資料庫中的ACID,我們思考一個問題:眾所周知,InnoDB的資料都是儲存在B-tree裡面的,修改後的資料到底要不要儲存在實際的B-tree葉子節點,session2是怎麼做到查詢出來的結果還是10,而不是20列?

三、MVCC實現原理

上述現象在資料庫中大家經常看到,但是資料庫到底是怎麼實現的,深究的人就不多了。 
其實原理很簡單,資料庫就是通過UNDO和MVCC來實現的。

通過DB_ROLL_PT 回溯查詢資料歷史版本

  • 首先InnoDB每一行資料還有一個DB_ROLL_PT的回滾指標,用於指向該行修改前的上一個歷史版本 

    當插入的是一條新資料時,記錄上對應的回滾段指標為NULL


更新記錄時,原記錄將被放入到undo表空間中,並通過DB_ROLL_PT指向該記錄。session2查詢返回的未修改資料就是從這個undo中返回的。MySQL就是根據記錄上的回滾段指標及事務ID判斷記錄是否可見,如果不可見繼續按照DB_ROLL_PT繼續回溯查詢。

通過read view判斷行記錄是否可見

Read View的資料結構,它有三個部分:

  • 當前活躍的事務列表
  • Tmin ,就是活躍事務的最小值
  • Tmax, 是系統中最大事務ID(不管事務是否提交)

具體的判斷流程如下:

  • RR隔離級別下,在每個事務開始的時候,會將當前系統中的所有的活躍事務拷貝到一個列表中(read view)
  • RC隔離級別下,在每個語句開始的時候,會將當前系統中的所有的活躍事務拷貝到一個列表中(read view) 
    並按照以下邏輯判斷事務的可見性。 

四、MVCC解決了什麼問題

  • MVCC使得資料庫讀不會對資料加鎖,select不會加鎖,提高了資料庫的併發處理能力
  • 藉助MVCC,資料庫可以實現RC,RR等隔離級別,使用者可以檢視當前資料的前一個或者前幾個歷史版本。保證了ACID中的I-隔離性。

參考:

https://juejin.im/entry/58f86815ac502e00638e1c97