mysql 原理 ~ 死鎖問題
一 鎖
1 鎖的定義
1 按照巨集觀角度
共享鎖【S鎖】
又稱讀鎖,若事務T對資料物件A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。
排他鎖【X鎖】
又稱寫鎖。若事務T對資料物件A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。這保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A。
2 按照微觀角度
recrod lock 行鎖 鎖定相關行
gap lock 間隙鎖 鎖定相關間隙,目的為了防止幻讀的發生,阻止insert操作
Next-Key(record lock+gap lock)
二 死鎖
1 死鎖的定義
當併發系統中不同執行緒出現迴圈資源依賴,涉及的執行緒都在等待別的執行緒釋放資源時,時,就會導致這幾個執行緒都進入無限等待的狀態,稱之為死鎖,死鎖會回滾成本較高的事務,執行成本較低的事務
2 死鎖檢測引數,預設開啟
innodb_deadlock_detect=ON
3 死鎖記錄日誌
show engine innodb status
LATEST DETECTED DEADLOCK
事務1
具體的sql語句
WAITING FOR THIS LOCK TO BE GRANTED//等待被授予的鎖
包括(table,index,wait for lock,no 923 n bits(類似這種鎖定的具體資料頁))
事務2
具體的sql語句
HOLDS THE LOCK(S) //擁有的鎖
包括(table,index,hold lock,no 923 n bits(類似這種鎖定的具體資料頁))
WAITING FOR THIS LOCK TO BE GRANTED//等待被授予的鎖
包括(table,index,wait for lock)
三 死鎖的分析
1 基礎分析
死鎖問題分為兩種,一種是必然會發生的死鎖場景,不過在業務研發上很少見 一種是高併發導致的死鎖場景,在業務研發上很常見,也是死鎖發生的最主要場景
2 基礎知識
1 mysql的加鎖方式 如果sql語句本身條件是主鍵,那麼會按照主鍵加行鎖 如果sql語句本身是輔助索引,那麼會先給輔助索引加鎖,再給輔助索引對應的主鍵加鎖,同時還有gap lock範圍鎖
2 mysql的加鎖順序 如果多個事務內的多個操作同時對同一資源申請加鎖,是根據事務內操作申請先後有序申請的,比如事務1申請X鎖->事務2申請X鎖->事務1又申請X鎖
3 獲取相關分析
1 是2個事務等待被授予的鎖和 具體的sql語句 和表結構
2 是全部的事務操作語句,因為死鎖日誌記錄並不包含全部的事務操作,只會記錄發生死鎖的最近的兩個sql操作
3 sql語句的explain 一定要根據sql語句的explain進行具體分析,不同的索引掃描行數是不同的,而mysql是逐行掃描加鎖的
這裡說一個update語句的加鎖順序
當Update SQL被髮給MySQL後,MySQL Server會根據where條件,讀取第一條滿足條件的記錄,然後InnoDB引擎會將第一條記錄返回,並加鎖 (current read-> for update)。待MySQL Server收到這條加鎖的記錄之後,會再發起一個Update請求,更新這條記錄(update -> X 鎖)。一條記錄操作完成,再 讀取下一條記錄,直至沒有滿足條件的記錄為止。因此,Update操作內部,就包含了一個當前讀。同理,Delete操作也一樣。
四 死鎖的案例
場景 1
1 insert併發導致的死鎖
事務1 insert into value(1) 事務1 rollback
事務2 insert into value(1)
事務3 insert into value(1)
2 死鎖日誌分析
1 insert在進行唯一值檢測的時候會加一個唯一性Insert Intention鎖,這個其實是(S)鎖,對發生衝突的事務X鎖,當事務1回滾後 事務2 和3 都申請了S鎖,後續需要申請了X鎖,但是由於事務2和3都擁有S鎖,互相等待不釋放,導致了死鎖,1個事務回滾釋放了S鎖,另一個事物申請成功
2 插入意向鎖需要加的鎖依次為 S NK, X IK, X RK
3 鎖等待 兩個事務都在等待 insert intention lock 鎖,也即是2階段的IK鎖
場景 2
1 update併發導致的死鎖
事務 1 update tab_test set state=1064,time=now() where state=1061 and time < date_sub(now(), INTERVAL 30 minute)
事務 2 update tab_test set state=1067,time=now () where id in (9921180)
2 死鎖日誌分析
1 加鎖可能應用在主鍵或者輔助索引上,此問題場景於高併發下的update導致的死鎖,有機率會出現
2 造成死鎖的原因是由於 事務1 在執行期間先在輔助索引加鎖,再加主鍵 事務2 先加主鍵先在主鍵上加鎖,再在輔助索引加鎖,最後造成互相持有資源不釋放
場景 3
1 混合事務導致的死鎖
事務 1 delete from table_1 where id=1 事務2 update table_2 set message='aa' where token='aaa'
update table_2 set message='aa' where token='aaa' delete from table_1 where id=1
2 死鎖日誌分析
1 mysql鎖申請是有序的
2 事務1 delete語句先拿到ID=1的行鎖 需要獲取token的輔助索引 事務2擁有token的輔助索引需要獲取ID=1的行鎖 互相等待持有不釋放
五 解決死鎖問題的通用方法
1 併發insert導致的死鎖(單事務)
1 減少唯一值檢測
2 降低重複值插入
3 降低插入頻率
2 併發update導致的死鎖 (單事務)
1 儘量採用主鍵進行更新
2 降低更新頻率
3 混合事務導致的死鎖 (混合事務)
1 儘量採用主鍵進行操作
2 分析事務內加鎖順序,改造程式,調整業務邏輯
3 降低事務操作頻率
4 特殊操作導致的死鎖
1 insert into select from 在目標表低峰業務期做
2 pt-osc同樣可能導致死鎖, 在目標表低峰業務期做
六 總結
1 死鎖問題經常爆發於高併發場景下,所以業務場景要考慮
2 不建議根據區分度較低的輔助索引值進行事務操作
3 排查死鎖問題最好能模擬死鎖場景問題