1. 程式人生 > >mysql 原理 ~ 死鎖問題

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  排查死鎖問題最好能模擬死鎖場景問題