Mysql MVCC機制

白露非霜發表於2021-07-31

之前有說到mysql事務隔離級別和鎖mysql事務隔離級別有:讀未提交,讀已提交,可重複讀,序列化。

可重複讀,當開啟事務之後,在此次事務中讀到的資料都不會變化(除開新增的資料(可重複讀隔離級別不能解決幻讀)),為什麼可重複讀隔離級別能做到這樣呢,這就不得不提mysqlMVCCMulti-Version Concurrency Control)多版本併發控制機制。對同一行記錄的讀寫操作不會通過加鎖來互斥。Mysql在讀已提交和可重複隔離級別下面都實現了MVCC機制

 

undo日誌版本鏈與read view機制

Mvcc機制主要是依靠undo日誌版本鏈與一致性檢視read view來實現的。

undo日誌版本鏈是指一資料被多個事務依次修改過後,在每個事務修改完後,Mysql會保留修改前的資料undo回滾日誌,通過事務ID(trx_id)roll_pointe(指向上一條undo日誌記錄)把這些undo日誌串聯起來形成一個歷史記錄版本鏈

begin/start transaction 命令並不是一個事務的起點,在執行到它們之後的第一個修改操作InnoDB表的語句, 事務才真正啟動,才會向mysql申請事務idmysql內部是嚴格按照事務的啟動順序來分配事務id的。

在可重複讀隔離級別,當事務開啟時,執行第一條查詢sql時會生成當前事務的一致性檢視read-view,注意並不是在開啟事務的時候生成一致性檢視,該檢視在事務結束之前都不會變化(如果是讀已提交隔離級別在每次執行查詢sql時都會重新生成一致性檢視),這個檢視由執行查詢時所有未提交事務ID陣列(陣列裡最小的idmin_id)和已建立的最大事務IDmax_id)組成,事務裡的任何sql查詢結果需要從對應版本鏈裡的最新資料開始逐條跟read-view做比對從而得到最終的快照結果。

如下圖:

 

 

如果此時這些事務都沒有提交,在這個時候我們進行查詢語句的時候,在生成的一致性檢視就是:[5,6,7],7 ,[5,6,7]查詢時未提交的事務ID陣列,執行此查詢的時候已建立的最大事務ID。

Undo日誌版本鏈和read view對比規則:

從版本鏈依次開始比對:

1.如果 版本鏈中記錄的行的(row) trx_id 小於檢視中的未提交事務陣列ID最小的值( trx_id<min_id ),表示這個版本是已提交的事務生成的,這個資料是可見的;

 2.如果 row 的 trx_id 大於陣列的最大ID( trx_id>max_id ),表示這個版本是由後來啟動的事務生成的,是不可見的(若 row 的 trx_id 就是當前自己的事務是可見的);

 3. row 的 trx_id 在檢視陣列中(min_id <=trx_id< max_id),表示這個版本是由還沒提交的事務生成的,不可見

 4.若 row 的 trx_id 不在檢視陣列中,表示這個版本是已經提交了的事務生成的,可見。

舉個例子:

開啟事務,按下面sql的執行循序執行sql

 

 

 

 結合上面undo日誌版本鏈,日誌版本鏈和此表sql的順序是一致的:

當我們執行查詢1的第一條查詢的時候生成一致性檢視: [5,6],7 在可重複讀隔離級別當前這次事務中的查詢只會生成一次檢視,不會再改變。開始執行比對規則。

版本鏈第一條資料trx_id5,命中比對規則3: 在檢視陣列(未提交的ID陣列)中,因此不可見;繼續比對trx_id7,命中規則4,那麼則可見。

查詢1剩下的兩條的sql,因為在第一次執行查詢已經生成了一致性檢視,雖然在步驟8的時候事務5提交了,但是並不會改變查詢1的一致性檢視,所以查詢1三條查詢結果都是一致,這也就實現可重複讀。

查詢2的查詢語句是在事務5提交之後執行的,因此它生成的一致性檢視和查詢1是不一樣的,它的檢視中未提交事務ID陣列只有事務6,因此它能夠查詢得到xiaohong55 事務1提交的結果。

如果是讀已提交,那麼就是每次執行查詢語句都會生成新的一致性檢視,試想查詢1如果在已提交的隔離級別下面,那麼執行最後一次的查詢生成的一致性檢視是和查詢2一致的,就能讀到事務5已提交的資料了。

對於刪除,會將版本鏈上最新的資料複製一份,然後將trx_id修改成刪除操作的 trx_id,同時在該條記錄的頭資訊(record header)裡的(deleted_flag刪除標記為true,來表示當前記錄已經被刪除,在查詢時按照上面的規則查到對應的記錄如果delete_flag標記為true,意味著記錄已被刪除,則不返回資料。

 

相關文章