1、MySQL可重複讀級別下,由於MVCC引發的BUG,下圖1爲相應的Java代碼,其中事務1的生命週期最長,循環開啓的事務二、三、4。。。與事務1存在併發問題html
圖1併發
解決方案:將方法userRemoteService.addUser和UserBaseContext.getUserBaseByUserId放在兩個方法中,避免事務的併發問題post
2、MVCC簡介:Multiversion Concurrency Control,多版本併發控制機制,行級鎖的一個變種, 可是它在不少狀況下避免了加鎖操做, 所以開銷更低,實現了非阻塞的讀操做,只在 READ COMMITTED
和 REPEATABLE READ
兩個隔離級別下工做,由於 READ UNCOMMITTED
老是讀取最新的數據行,而SERIALIZABLE
則會對全部讀取的行都加鎖性能
3、數據行隱藏字段設計
6字節的DATA_TRX_ID 標記了最新更新這條行記錄的transaction id,每處理一個事務,其值自動+13d
7字節的DATA_ROLL_PTR 指向當前記錄項的rollback segment的undo log記錄,找以前版本的數據就是經過這個指針指針
6字節的DB_ROW_ID,當由innodb自動產生彙集索引時,彙集索引包括這個DB_ROW_ID的值,不然彙集索引中不包括這個值.,這個用於索引當中code
DELETE BIT位用於標識該記錄是否被刪除,這裏的不是真正的刪除數據,而是標誌出來的刪除。真正意義的刪除是在commit的時候htm
對於有有三個字段id、name、balance的表,其中id爲主鍵,實際的擁有的列以下blog
圖2
4、具體的執行過程:
SELECT
Innodb檢查每行數據,確保他們符合兩個標準:
一、InnoDB只查找版本早於當前事務版本的數據行(也就是數據行的版本必須小於等於事務的版本),這確保當前事務讀取的行都是事務以前已經存在的,或者是由當前事務建立或修改的行
二、行的刪除操做的版本必定是未定義的或者大於當前事務的版本號,肯定了當前事務開始以前,行沒有被刪除
符合了以上兩點則返回查詢結果。
INSERT
InnoDB爲每一個新增行記錄當前系統版本號做爲建立ID,該操做沒有回滾指針,由於不存在歷史版本
DELETE
InnoDB爲每一個刪除行的記錄當前系統版本號做爲行的刪除ID。
UPDATE
InnoDB複製了一行。這個新行的版本號使用了系統版本號。它也把系統版本號做爲了刪除行的版本
事務執行過程當中,只有在第一次真正修改記錄時(好比使用INSERT、DELETE、UPDATE語句),纔會被分配一個單獨的事務id,這個事務id是遞增的,下面以update爲例說明
begin->用排他鎖鎖定該行->記錄數據行數據快照到undo log,將修改前的行標記爲刪除,寫事務編號->新增行保存修改後的值,寫事務編號,回滾指針指向undo log中的修改前的行->將undo log寫到磁盤->數據寫磁盤->commit
圖3
優勢:
保存這兩個額外系統版本號,使大多數讀操做均可以不用加鎖。這樣設計使得讀數據操做很簡單,性能很好。
缺點:
每行紀錄都須要額外的存儲空間,須要作更多的行檢查工做,以及一些額外的維護工做。
5、read view
1.判斷當前版本數據項是否可見
2.提交讀的隔離級別下,事務開始後到結束前,每次讀取數據都會生成一個read view,而可重複讀的隔離級別,只有事務開始後第一次讀取數據,才生成read view
2.在innodb中, 每建立一個新事務, 存儲引擎都會將當前系統中的活躍事務列表建立一個副本(read view
), 副本中保存的是系統中當前不該該被本事務看到的其餘事務id列表
3.當用戶在事務中要讀取某行記錄的時候, innodb會將該行當前的版本號與該read view進行比較
4.比較流程:read view中最先的事務id爲tmin,最遲的事務id爲tmax,當前事務id爲t0
圖4
參考文章
https://www.cnblogs.com/williamjie/p/9492810.htmlhttps://www.jianshu.com/p/db334404d909https://juejin.im/post/5c9b1b7df265da60e21c0b57