目錄
1.什么是mvcc?
2.問題引入
3. MVCC實現原理?
3.1 隱藏字段
3.2?undo log 日志
3.2.1?undo log版本鏈
3.3?readview
3.3.1?當前讀
?編輯
3.3.2?快照讀
3.3.3 ReadView中4個核心字段
3.3.4 版本數據鏈訪問的規則(了解)
4.面試題
前言:mysql中的Redolog日志保證了事務的持久性,Undolog保證了事務的原子性和一致性(回滾)
那事務的隔離性是怎么保證的呢?
答:
1. 鎖:排他鎖(如一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖)
2. mvcc:多版本并發控制
這里主要講一下mvcc
1.什么是mvcc?
多版本并發控制。指維護一個數據的多個版本,使得讀寫操作沒有沖突
2.問題引入
下面是一行數據,事務2、3、4都并發的對這行數據進行修改,而事務5要查詢兩次這行的最新數據,問查詢到的是哪一次事務執行到的結果?
如果你看到這里不知道結果,請你繼續往下看
3. MVCC實現原理?
MVCC的具體實現,主要依賴于數據庫記錄中的隱式字段、undo log日志、readView。? ? ??
3.1 隱藏字段
3.2?undo log 日志
回滾日志,在insert、update、delete的時候產生的便于數據回滾的日志。
當insert的時候,產生的undo log日志只在回滾時需要,在事務提交后,可被立即刪除。
而update、delete的時候,產生的undo log日志不僅在回滾時需要,mvcc版本訪問也需要,不會立即被刪除。
3.2.1?undo log版本鏈
不同事務或相同事務對同一條記錄進行修改,會導致該記錄的undolog生成一條記錄版本鏈表,鏈表的頭部是最新的舊記錄,鏈表尾部是最早的舊記錄。
下面是事務
3.3?readview
ReadView(讀視圖)是快照讀。是一個事務在執行 快照讀(SELECT 等語句) 時生成的,它保存了4個關鍵字段(3.4.3 有介紹)
3.3.1?當前讀
讀取的是記錄的最新版本,讀取時還要保證其他并發事務不能修改當前記錄,會對讀取的記錄進行加鎖。對于我們日常的操作,
如:select ... lock in share mode(共享鎖),select ... for update、update、insert、delete(排他鎖)都是一種當前讀。
3.3.2?快照讀
簡單的select(不加鎖)就是快照讀,快照讀,讀取的是記錄數據的可見版本,有可能是歷史數據,不加鎖,是非阻塞讀。
讀已提交 RC (Read Committed:每次select,都生成一個快照讀。
可重復讀 RR (Repeatable Read)(MySQL 的默認隔離級別):開啟事務后第一個select語句才是快照讀的地方。
3.3.3 ReadView中4個核心字段
ReadView(讀取視圖) 是一個事務在執行 快照讀(SELECT 等語句) 時生成的,它保存了以下關鍵字段:
還以下面的例子來解釋:
事務五第一次查詢id為30的記錄,此時:
m_ids:當前活躍的事務ID集合是事務三、四、五。因為在事務五第一次查詢之前事務二已經提交了(事務執行完畢)。
min_trx_id:最小活躍事務ID是事務三的id為3
max_trx_id:預分配事務ID,當前最大事務ID+1(因為事務ID是自增的),為事務5的id+1是6?
creator_trx_id:ReadView創建者的事務ID ,即為是事務五的id 5
不同的隔離級別,生成ReadView的時機不同:
讀已提交 RC READ COMMITTED :在事務中每一次執行快照讀時生成ReadView。
可重復讀 RRREPEATABLE READ:僅在事務中第一次執行快照讀時生成ReadView,后續復用該ReadView。
3.3.4 版本數據鏈訪問的規則(了解)
①. trx_id ?== creator_trx_id ? 可以訪問該版本
②. trx_id < min_trx_id ? 可以訪問該版本
③. trx_id > max_trx_id ? ?不可以訪問該版本
④. min_trx_id <= trx_id <= max_trx_id ? ?如果trx_id不在m_ids中是可以訪問該版本的
還以下面5個事務來舉例子:
還以事務五第一次查詢舉例分析可以得到以下結論
上述 ①~④ 是 InnoDB 在使用 MVCC 機制進行快照讀(Snapshot Read)時判斷一個版本是否可見的規則,目的是為了實現事務隔離性(尤其是可重復讀)。
核心依據是將數據行上的 trx_id
與當前事務的 ReadView
(讀取視圖)中的一些字段做比較,判斷是否可以“看到”這個版本。
詳細解釋:
①.當前事務的id(未知),等于事務創建者的id(這里是事務五),所以當前事務是事務五,說明當前數據是由“我自己”這個事務修改的。MVCC 當然允許自己看到自己改過的數據(包括未提交的),以確保 讀到的是自己操作后的最新快照。
②.當前事務的id(未知),小于最小活躍事務ID(這里是事務三),所以當前事務是事務二,而事務二已經提交,
-
說明這個事務在我生成 ReadView 之前就已經提交了。
-
因此是 對我“可見”的歷史版本數據。
③.當前事務的id(未知),大于最大活躍事務ID+1(這里是事務五),所以當前事務是事務六,事務六在此時ReadView 生成時,事務六還沒有創建
-
所以它對我來說是“未來”的數據。
-
根據事務隔離的原則,我不能看見這個數據版本。
④. min_trx_id <= trx_id <= max_trx_id,這個事務是在我生成 ReadView 時是活躍的(ID 在 min~max 之間),但它不在活躍事務列表 m_ids
中,說明它已經 提交完成了,因此我可以看見它。和②.表達的意思一樣
不同的隔離級別,生成ReadView的時機不同:
讀已提交 RC READ COMMITTED :在事務中每一次執行快照讀時生成ReadView。
可重復讀 RRREPEATABLE READ:僅在事務中第一次執行快照讀時生成ReadView,后續復用該ReadView。
舉例: