- MVCC是什麼呢?
MVCC其實就是一個多版本併發控制,即多個不同版本的資料實現併發控制的技術,其基本思想是為每次事務生成一個新版本的資料,
在讀資料時選擇不同版本的資料即可以實現對事務結果的完整性讀取。
- MVCC主要有什麼作用呢?
提高併發讀寫效能,操作時會生成事務id
1》每條記錄都會儲存兩個隱藏列:【trx_id】(事務id)和roll_pointer(回滾指標
2》每次操作都會生成一條undo_log日誌,回滾指標指向前一條記錄
查詢的時候會讀取出【read-view】:[未提交的事務id]陣列+最大事務id,並根據read-view從undo_log日誌中最新的記錄一次往下找,查詢規則如下:
①從最新記錄開始查詢:
如果當前記錄:事務id < 未提交事務的最小id,則可讀
如果當前記錄:最小id <= 事務id <= 事務的最小id,則判斷事務id是否在未提交事務id的陣列中,若在則不可讀(如果只有自己還是可讀)
如果當前記錄:事務id > 事務的最大id,則不可讀
②可重複讀返回的是第一次查詢生成的【read-view】,讀已提交每次都會重新生成一個新的【read-view】
MVCC只能實現讀已提交和可重複讀;如果是讀未提交,那麼每次查詢都能獲取最新的修改值。
- Don‘t BB,看個案例先回顧一下事物的隔離級別:
那麼事務A在提交前後,事務B讀取到的x值是什麼樣的呢?答案是:事務B在不同隔離級別下,讀取到的值不一樣。
1》如果隔離級別是讀未提交(Read-Uncommited),兩次讀到的資料都是20,(個人理解讀未提交,就是事務A未提交的修改都可以讀到)
2》如果隔離級別是讀已提交(Read-Committed),第一次讀取到的資料是10,第二次讀取到的資料是20(我個人理解讀已提交,就是隻有事務A已提交的修改才可以讀到)
3》如果隔離級別是可重複度(Repeatable-Read),兩次讀到的資料都是20(我個人有理解,讀已提交就是事務B有一個很強的隔離性,只要在同一事務內,讀到的資料都是相同的)
-
當執行查詢SQL時,會生成一致性檢視叫【read-view】,它由執行查詢時所未提交事務id陣列(陣列裡最小的事務id為【min_id】和已建立的最大事務id為【max_id】組成),
查詢的資料結果要跟【read-view】做比較,從而得到快照結果。話不多少,直接上個案例:
事務1 | 事務2 | 事務3 | 事務4 |
update table set name = 'A' where id = 1 | |||
update table set name = 'B' where id = 1 | |||
commit | update table set name = 'C' where id = 1 | ||
update table set name = 'C' where id = 1 | select name from table where id = 1 ; read-view:[1,3] 3 | ||
commit | |||
commit | select name from table where id = 1 |
Undo日誌如下:
事務4在第一次查詢過程是這樣:
1》先比對【undo_log①】,事務id為1,read-view為[1,3] 3,不符合條件,繼續向下比對; 2》比對【undo_log②】,事務id為3,事務id > 事務的最大id,所以也不可讀; 3》比對【undo_log③】,事務id為2,最小id <= 事務id <= 事務的最小id,並且不在未提交事務id陣列中,所以可以,所以查詢出來的【name = B】; 我們可以根據表格看出,事務2已提交,所以結果就應該B,正確。
- 讀已提交是如何使用【MVCC】實現的呢?
每次查詢的時候生成一個新的【read-view】,然後去【undo】日誌中尋找符合結果的一條資料。
所以事務4對應的【read-view】應該為:[1] 3,第二次查詢【name = 'C'】
- 可重複讀是如何使用【MVCC】實現的呢?
因為每次查詢的時候都會生成一個新的【read-view】,但是如果是可重複讀的時候,
第一次查詢的時候生成一個【read-view】,後面查詢的時候便會複用第一次的【read-view】,然後找出一個可讀的資料
所以事務4對應的【read-view】應該為:[1,3] 3,第二次查詢【name = 'B'】
- 不過可序列化又是如何實現的呢?
序列化會在讀取的每一行資料上都加鎖(這個意思是部分鎖,但不會鎖表)
序列讀(Serializable):完全序列化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞