- SERIALIZABLE 串行化
- MVCC + Next-Key Lock
幻讀:
幻讀指的是一個事務在進行一次查詢之后發現某個記錄不存在,然后會根據這個結果進行下一步操作,此時如果另一個事務成功插入了該記錄,那么對於第一個事務而言,其進行下一步操作(比如插入該記錄)的時候很可能會報錯。從事務使用的角度來看,在檢查一條記錄不存在之后,其進行插入應該完全沒問題的,但是這里卻拋出主鍵沖突的異常。
簡單來說:事務A的兩次讀之間有其他事務寫操作,比如事務A統計年齡 > 30,當A兩次讀數據之間其他事務新添加了記錄,所以事務A第二次讀取到的數據突然多了一個,仿佛出現了幻覺一般,這就是一種幻讀
串行化:
- 事務在讀操作時,先加表級別的共享鎖,直到事務結束才釋放
- 事務在寫操作時,先加表級別的排它鎖,直到事務結束才釋放
串行化鎖定了整張表,幻讀不存在的!!!
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
多版本並發MVCC:
InnoDB 的 MVCC, 是 通過 在 每 行 記錄 后面 保存 兩個 隱藏 的 列 來 實現 的。 這 兩個 列, 一個 保存 了 行的 創建 時間, 一個 保存 行的 過期 時間( 或 刪除 時間)。 當然 存儲 的 並不是 實際 的 時間 值, 而是 系統 版 本號( system version number)。 每 開始 一個 新的 事務, 系統 版本 號 都會 自動 遞增。 事務 開始時 刻 的 系統 版 本號 會 作為 事務 的 版 本號, 用來 和 查詢 到 的 每 行 記錄 的 版本 號 進行 比較。
MVCC 只在 REPEATABLE READ 和 READ COMMITTED 兩個 隔離 級別 下 工作。
MVCC快照讀需滿足條件:
- InnoDB 只 查找 版本 早於 當前 事務 版本 的 數據 行( 也就是, 行的 系統 版本 號 小於 或 等於 事務 的 系統 版 本號), 這樣 可以 確保 事務 讀 取的 行, 要么 是在 事務 開始 前 已經 存在 的, 要么 是 事務 自身 插入 或者 修 改過 的。
- 行的 刪除 版本 要么 未定義, 要么 大於 當前 事務 版 本號。 這可 以 確保 事務 讀取 到 的 行, 在 事務 開始 之前 未被 刪除。
MVCC解決了基於快照讀下的幻讀,事務 讀 取的 行, 要么 是在 事務 開始 前 已經 存在 的, 要么 是 事務 自身 插入 或者 修 改過 的。
並不會讀到其他事務的寫操作 !!!
但是MVCC無法解決當前讀下的幻讀。
當前讀:
select * from tb where ? lock in share mode; select * from tb where ? for update;
for update:IX鎖(意向排它鎖),即在符合條件的rows上都加了排它鎖
lock in share mode:是IS鎖(意向共享鎖),即在符合條件的rows上都加了共享鎖
排它鎖:X鎖、 寫鎖,事務A對一個資源加了X鎖后只有A本身能對該資源進行讀和寫操作,其他事務對該資源的讀和寫操作都將被阻塞,直到A釋放鎖為止
共享鎖:S鎖、 讀鎖, 事務A鎖定的數據其他事務可以共享讀該資源,但不能寫,直到事務A釋放
InnoDB 默認開啟間隙鎖, innodb_locks_unsafe_for_binlog參數表示 是否禁用Gap Lock間隙鎖,默認值0 ,啟動間隙鎖
測試時,設置參數為1 禁用間隙鎖,演示幻讀場景
SHOW VARIABLES LIKE '%innodb_locks_unsafe_for_binlog%'
start transaction; select * from tb where id>100 for update; update tb set product_num=product_num-1 where id>100;
由於Mysql Server會針對update和delete操作里面的where條件查找滿足條件的記錄
(查找的不是快照)然后Innodb引擎會返回的滿足條件的加鎖記錄,
當其他事務進行Insert 操作后,進行一次當前讀時,就會讀到其他事務 Insert 記錄,可以明顯的發現其會導致幻讀
-------------------------------------------------------------------------------------------------------------------------------------------------------
Next-Key Lock是Gap Lock(間隙鎖)和Record Lock(行鎖)的結合版,都屬於Innodb的鎖機制
select * from tb where id>100 for update;
- 主鍵索引 id 會給 id=100 的記錄加上 record行鎖
- 索引 id 上會加上 gap 鎖,鎖住 id(100,+無窮大)這個范圍
其他事務對 id>100 范圍的記錄讀和寫操作都將被阻塞
插入 id=1000的記錄時候會命中索引上加的鎖會報出事務異常;
Next-Key Lock會確定一段范圍,然后對這個范圍加鎖,保證A在where的條件下讀到的數據是一致的,因為在where這個范圍其他事務根本插不了也刪不了數據,都被Next-Key Lock鎖堵在一邊阻塞掉了。
施瓦茨(Baron Schwartz); 扎伊采夫(Peter Zaitsev); 特卡琴科(Vadim Tkachenko). 高性能MySQL(第3版)