阿里Java一面:剛談到MySQL事務特性,講講InnoDB如何保證事務特性

回滾日誌 undo log
爲了滿足事務的原子性,在操作任何數據之前,首先將數據備份到一個地方(這個存儲數據備份的地方稱爲Undo Log),然後進行數據的修改。如果出現了錯誤或者用戶執行了 ROLLBACK語句,系統可以利用Undo Log中的備份將數據恢復到事務開始之前的狀態。

undo log實現多版本併發控制(MVCC)來輔助保證事務的隔離性。

回滾日誌不同於重做日誌,它是邏輯日誌,對數據庫的修改都邏輯的取消了。當事務回滾時,它實際上做的是與先前相反的工作。對於每個INSERT,InnoDB存儲引擎都會完成一個DELETE;對於每個UPDATE,InnoDB存儲引擎都會執行一個相反的UPDATE。

事務提交後並不能馬上刪除undo log,這是因爲可能還有其他事務需要通過undo log 來得到行記錄之前的版本。故事務提交時將undo log 放入一個鏈表中,是否可以刪除undo log 根據操作不同分以下2種情況:

Insert undo log: insert操作的記錄,只對事務本身可見,對其他事務不可見(這是事務隔離性的要求),故該undo log可以在事務提交後直接刪除。不需要進行 purge操作。

update undo log:記錄的是對 delete和 update操作產生的 undo log。該undo log可能需要提供MVCC機制,因此不能在事務提交時就進行刪除。提交時放入undo log鏈表,等待 purge線程進行最後的刪除。


事務的隔離性的實現原理就是鎖,因而隔離性也可以稱爲併發控制、鎖等。事務的隔離性要求每個讀寫事務的對象對其他事務的操作對象能互相分離。再者,比如操作緩衝池中的LRU列表,刪除,添加、移動LRU列表中的元素,爲了保證一致性那麼就要鎖的介入。

鎖的類型

InnoDB主要有2種鎖:行級鎖,意向鎖

行級鎖:

共享鎖(讀鎖 S),允許事務讀一行數據。事務拿到某一行記錄的共享S鎖,纔可以讀取這一行,並阻止別的事務對其添加X鎖。共享鎖的目的是提高讀讀併發。

排它鎖(寫鎖 X),允許事務刪除一行數據或者更新一行數據。事務拿到某一行記錄的排它X鎖,纔可以修改或者刪除這一行。排他鎖的目的是爲了保證數據的一致性。

行級鎖中,除了S和S兼容,其他都不兼容。

意向鎖:

意向共享鎖(讀鎖 IS ),事務想要獲取一張表的幾行數據的共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(寫鎖 IX),事務想要獲取一張表中幾行數據的排它鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
解釋一下意向鎖
The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.
意向鎖的主要用途是爲了表達某個事務正在鎖定一行或者將要鎖定一行數據。e.g:事務A要對一行記錄r進行上X鎖,那麼InnoDB會先申請表的IX鎖,再鎖定記錄r的X鎖。在事務A完成之前,事務B想要來個全表操作,此時直接在表級別的IX就告訴事務B需要等待而不需要在表上判斷每一行是否有鎖。意向排它鎖存在的價值在於節約InnoDB對於鎖的定位和處理性能。另外注意了,除了全表掃描以外意向鎖都不會阻塞。

鎖的算法
InnoDB有三種行鎖的算法:

Record Lock:單個行記錄上的鎖
Gap Lock:間隙鎖,鎖定一個範圍,而非記錄本身
Next-Key Lock:結合Gap Lock和Record Lock,鎖定一個範圍,並且鎖定記錄本身。主要解決的問題是REPEATABLE READ隔離級別下的幻讀。可以參考文章瞭解事務隔離級別的相關知識點。
這裏主要講一下Next-Key Lock,利用Next-key Lock鎖定的不是單個值而是一個範圍,他的目的就是爲了阻止多個事務將記錄插入到同一範圍內從而導致幻讀。

注意了,如果走唯一索引,那麼Next-Key Lock會降級爲Record Lock,即僅鎖住索引本身,而不是範圍。也就是說Next-Key Lock前置條件爲事務隔離級別爲RR且查詢的索引走的非唯一索引、主鍵索引。

下面我們用個例子詳細說一下。

首先建立一張表:

阿里Java一面:剛談到MySQL事務特性,講講InnoDB如何保證事務特性
事務A執行如下語句:

SELECT * FROM T WHERE f_id = 3 FOR UPDATE
這時SQL語句走非唯一索引,因此使用Next-Key Locking加鎖,並且有2個索引,其需要分別進行鎖定。

對於聚集索引,其僅對id等於5的索引加上Record Lock。而對於輔助索引,其加上Next-Key Lock,鎖定了範圍(1,3),特別需要注意的是,InnoDB存儲引擎還會對輔助索引下一個鍵值加上Gap Lock,即範圍(3.6)的鎖。

所以如果在新session中執行如下語句都會報錯[Err] 1205 - Lock wait timeout exceeded; try restarting transaction:

阿里Java一面:剛談到MySQL事務特性,講講InnoDB如何保證事務特性
此時想象一下,事務A鎖定了f_id =5 的記錄, 正常會有個gap lock,鎖住(5,6),那麼如果沒有(5,6)的gap鎖,那麼用戶可以插入索引 f_id 爲5的記錄,這樣事務A再次查詢就會返回一個不同的記錄,也就導致了幻讀的產生。

同理,如果我們事務A執行的是select * from T where f_id = 10 FOR UPDATE,在表裏查不到數據,但是基於Next-Key Lock會鎖住(8,+∞),我們執行INSERT INTO T SELECT 6,11是無法插入成功的,這就從根本上解決了幻讀問題。

最後
不管多忙,每天給自己預留至少半小時的學習時間,拒絕做代碼垃圾的搬運工!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章