日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql死锁场景汇总整理

發布時間:2025/3/19 数据库 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql死锁场景汇总整理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

簡述

行鎖導致死鎖

gap lock/next keys lock導致死鎖

index merge導致死鎖

唯一索引沖突導致死鎖

總結


簡述

本文死鎖場景皆為工作中遇到(或同事遇到)并解決的死鎖場景,寫這篇文章的目的是整理和分享,歡迎指正和補充,本文死鎖場景包括:

  • 行鎖導致死鎖
  • gap lock/next keys lock導致死鎖
  • index merge 導致死鎖
  • 唯一索引沖突導致死鎖

:以下場景隔離級別均為默認的Repeatable Read;

行鎖導致死鎖

前提:表 t_user 的 uid 字段創建了唯一索引,并擁有可更新字段age。
場景復現

行鎖導致死鎖


死鎖原因詳解

  • 兩個事務執行過程時間上有交集,并且過程發生在兩者提交之前
  • 事務1更新uid=1的記錄,事務2更新uid=2的記錄,在RR級別,由于uid是唯一索引,因此兩個事務將分別持有uid=1和2所在行的獨占鎖
  • 事務1執行到第二條更新語句時,發現uid=2的行被鎖住,進入阻塞等待鎖釋放;
  • 事務2執行到第二條語句時發現uid=1的行被鎖,同樣進入阻塞
  • 兩個事務互相等待,死鎖產生。
  • 相應業務案例和解決方案
    該場景常見于事務中存在for循環更新某條記錄的情況,死鎖日志顯示lock_mode X locks rec but not gap waiting(即行鎖而非間隙鎖),解決方案:

  • 避免循環更新,優化為一條where鎖定要更新的記錄批量更新
  • 如果非要循環更新,嘗試取消事務(能接受的話),即每一條更新為一個獨立的事務
  • gap lock/next keys lock導致死鎖

    表結構:

    CREATE TABLE `t_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`age` int(3) DEFAULT NULL,PRIMARY KEY (`id`),KEY `udx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;

    場景復現
    首先查詢表中目前存在的記錄:

    執行兩個事務的操作:

    死鎖原因分析

  • 事務1執行delete age = 27,務2執行delete age = 31,在RR級別,操作條件不是唯一索引時,行鎖會升級為next keys lock(可以理解為間隙鎖),因此事務1鎖住了25到27和27到29的區間,事務2鎖住了29到31的區間
  • 事務1執行insert age = 30,等待事務2釋放鎖
  • 事務2執行insert age = 28,等待事務1釋放鎖
  • 死鎖產生,死鎖日志顯示lock_mode X locks gap before rec insert intention waiting
  • 解決方案

  • 降低事務隔離級別到Read Committed,該隔離級別下間隙鎖降級為行鎖,可以減少死鎖發生的概率
  • 避免這種場景- -
  • index merge導致死鎖

    t_user結構改造為:

    CREATE TABLE `t_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`age` int(11) DEFAULT NULL,`zone_id` bigint(20) DEFAULT NULL,`username` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_age` (`age`),KEY `idx_zone_id` (`zone_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

    場景復現操作(幾率不高)

    假設存在以下數據

    idzone_iduidusername
    111""
    212""
    321""
    422""

    死鎖分析

  • 在符合場景前提的情況下(即表數據量較大,index_merge未關閉),通過explain分析update t_user where zone_id = 1 and uid = 1可以發現type是index_merge,即會用到zone_id和uid兩個索引
  • 上鎖的過程為:
  • 事務1
    ① 鎖住zone_id=1對應的間隙鎖: zoneId in (1,2)
    ② 鎖住索引zone_id=1對應的主鍵索引行鎖id = [1,2]
    ③ 鎖住uid=1對應的間隙鎖: uid in (1, 2)
    ④ 鎖住uid=1對應的主鍵索引行鎖: id = [1, 3]
    事務2
    ① 鎖住zone_id=2對應的間隙鎖: zoneId in (1,2)
    ② 鎖住索引zone_id=2對應的主鍵索引行鎖id = [3,4]
    ③ 鎖住uid=2對應的間隙鎖: uid in (1, 2)
    ④ 鎖住uid=2對應的主鍵索引行鎖: id = [2, 4]

  • 如果兩個事務上鎖的順序相反,則有一定的概率出現死鎖。另外,index_merge的形式鎖住了很多不符合條件的行,浪費了資源。一般死鎖日志打印的信息為:lock_mode X locks rec but not gap waiting Record lock
  • 解決方案:創建聯合索引,使執行計劃只會用到一個索引。

    唯一索引沖突導致死鎖

    測試表結構:

    CREATE TABLE `t_sample` (`id` bigint(29) NOT NULL AUTO_INCREMENT,`uid` int(11) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `uk_uid` (`uid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

    場景復現操作

    image.png


    死鎖分析

  • 三個事務分別嘗試插入uid=1的數據,其中事務1先于后兩個事務
  • 由于是唯一索引,所以后兩個事務會出現唯一鍵沖突,但是事務1并未立即提交,因此不會報錯,而是將事務一insert的隱式鎖升級為顯式鎖
  • 事務二和事務三為了判斷是否出現唯一鍵沖突,必須進行一次當前讀(select...lock in share mode),加的鎖是GAP S鎖,所以進入阻塞,等待事務一釋放鎖
  • 事務一回滾,此時事務二和事務三成功獲取記錄上的GAP S鎖,并繼續執行插入操作
  • 插入則需要依次請求插入意向鎖,而插入意向鎖和GAP S鎖沖突,因此兩個事務相互等待,形成死鎖
  • 解決辦法:盡量避免這種插入又回滾的場景。

    總結

    避免死鎖的原則:

    • 建立合適的索引,減小鎖的粒度
    • 選擇合適的事務隔離級別
    • 大事務拆成小事務,一個事務中的鎖盡量少

    總結

    以上是生活随笔為你收集整理的mysql死锁场景汇总整理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。