MySQL 加鎖和死鎖解析
目錄
- 產生死鎖的必要條件
- 鎖是加在那裡的?
- 操作與加鎖的對照關係
- Insert
- Delete
- Update
- GAP鎖
- 什麼時候加next-key lock?
- Insert Intention Lock
- 總結
產生死鎖的必要條件
- 多個併發事務(2個或者以上)
- 每個事物都持有了鎖(或者是已經在等待鎖)
- 每個事務都需要再繼續持有鎖(為了完成事務邏輯,還必須更新更多的行)
事物之間產生加鎖的迴圈等待,形成死鎖
常規鎖模式
- LOCK_S(讀鎖,共享鎖)
LOCK_X(寫鎖,排它鎖)
鎖的屬性
- LOCK _REC_NOT_GAP(鎖記錄)
- LOCK_GAP(鎖記錄前的GAP)
- LOCK_ORDINARY(同時鎖記錄+記錄前的GAP,Next key鎖)
LOCK_INSERT_INTETION(插入意向鎖)
鎖組合(屬性+模式)
可以任意組合
鎖衝突矩陣
鎖是加在那裡的?
根據主鍵查詢-鎖加在主鍵上
如 begin;select * from tt_copy where id=4 for update;
加鎖情況index PRIMARY of table
test
.tt_copy
trx id 1101588 lock_mode X locks rec but not gap
根據普通索引查詢-鎖加在普通索引和主鍵上
如 begin;select * from tt_copy force index(idx_a) where a=4 for update;
加鎖情況index idx_a of table
test
.tt_copy
trx id 1101590 lock_mode X locks rec but not gap
index PRIMARY of tabletest
.tt_copy
trx id 1101590 lock_mode X locks rec but not gap
操作與加鎖的對照關係
以下沒特殊說明都為RC隔離級別
Insert
- 無Unique key,插入後 :無論RC或RR隔離級別都是對主鍵加 LOCK_X+LOCK_REC_NOT_GAP
有Unique key
插入前,唯一約束檢查:LOCK_S+LOCK_ORDINARY
插入前,插入的位置有GAP鎖:LOCK_INSERT_INTETION
插入後,新資料插入:LOCK_X+LOCK_REC_NOT_GAP
Delete
滿足刪除條件的所有記錄:LOCK_X+LOCK_REC_NOT_GAP
Update
Update操作分解
- Step 1:定位到 下一條滿足查詢條件的記錄(查詢過程,類似於Select/Delete)
- Step 2:刪除當前定位到的記錄(標記為刪除狀態)
- Step 3:拼裝更新後項,根據更新後項定位到 新的插入位置
- Step 4:在新的插入位置,判斷是否存在 Unique 衝突( 存在Unique Key 時)
- Step 5:插入更新後項(不存在Unique衝突時)
- Step 6: 重複Step 1 到Step 5 的操作,直至掃描完整個查詢範圍
Update操作分析
- Step 1,Step 2:Delete
- Step 3,Step 4,Step 5:Insert
Update
無Unique key:
- 查詢範圍中的所有記錄,LOCK_X + LOCK_REC_NOT_GAP
有Unique key:
- 查詢滿足條件的記錄:查詢範圍內的所有記錄, LOCK_X + LOCK_REC_NOT_GAP
- 更新後項存在唯一性衝突:衝突項上的加鎖,LOCK_S + LOCK_ORDINARY
- 更新後項不存在唯一性衝突: 更新位置後項加鎖,LOCK_S + LOCK_GAP (省略)
- 實際更新操作:可看做插入了一條新紀錄,LOCK_X + LOCK_REC_NOT_GAP
GAP鎖
那些操作會加GAP鎖?
- Read Committed (RC) ) :Unique Key 唯一約束檢查;Purge操作;
- Repeatable Read (RC ):RC的基礎上,所有需要加鎖的索引範圍掃描和索引查詢(Update/Delete…)
- 還有一種會加GAP鎖:RR隔離級別下,對有唯一索引的表執行insert on duplicate update操作,除了會對新插入的記錄加x not gap外,還會對相鄰記錄加x gap
如何去掉GAP鎖?
change the transaction isolation level to READ COMMITTED or enable the innodb_locks_unsafe_for_binlog system variable (which is now deprecated)
什麼時候加next-key lock?
By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows
Insert Intention Lock
An insert intention lock is a type of gap lock set by INSERT operations prior to(在...之前) row insertion.
總結
• 原則之一
- 要分析一個死鎖,必須深入業務,瞭解整個事務的邏輯(閉門無法造車)
• 原則之二`
- GAP鎖很複雜,為了減少GAP鎖,減少GAP導致的死鎖,儘量選擇Read Committed隔離級別(RC + row based binlog,基本上能夠解決所有問題,無需使用Repeatable Read)
- 適當的 減少Unique 索引,能夠減少GAP鎖導致的死鎖(根據業務情況而定)
• 原則之三
- 在MySQL 中,以不同索引的過濾條件, 來操作相同的記錄(Update/Delete ),很容易產生死
鎖。
• 原則之四
- RC隔離級別下,如果死鎖中出現Next Key(Gap鎖),說明表中一定存在unique索引
- 多語句事務產生的死鎖,確保每條語句操作記錄的順序性,能夠極大減少死鎖
本文大多數都整理自《死鎖-何登成 - 管中窺豹——MySQL(InnoDB)死鎖分析之道》<