mysql死锁场景汇总整理
目錄
簡述
行鎖導致死鎖
gap lock/next keys lock導致死鎖
index merge導致死鎖
唯一索引沖突導致死鎖
總結
簡述
本文死鎖場景皆為工作中遇到(或同事遇到)并解決的死鎖場景,寫這篇文章的目的是整理和分享,歡迎指正和補充,本文死鎖場景包括:
- 行鎖導致死鎖
- gap lock/next keys lock導致死鎖
- index merge 導致死鎖
- 唯一索引沖突導致死鎖
注:以下場景隔離級別均為默認的Repeatable Read;
行鎖導致死鎖
前提:表 t_user 的 uid 字段創建了唯一索引,并擁有可更新字段age。
場景復現:
行鎖導致死鎖
死鎖原因詳解:
相應業務案例和解決方案:
該場景常見于事務中存在for循環更新某條記錄的情況,死鎖日志顯示lock_mode X locks rec but not gap waiting(即行鎖而非間隙鎖),解決方案:
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;場景復現:
首先查詢表中目前存在的記錄:
執行兩個事務的操作:
死鎖原因分析:
解決方案:
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;場景復現操作(幾率不高):
假設存在以下數據:
| 1 | 1 | 1 | "" |
| 2 | 1 | 2 | "" |
| 3 | 2 | 1 | "" |
| 4 | 2 | 2 | "" |
死鎖分析:
事務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]
解決方案:創建聯合索引,使執行計劃只會用到一個索引。
唯一索引沖突導致死鎖
測試表結構:
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
死鎖分析:
解決辦法:盡量避免這種插入又回滾的場景。
總結
避免死鎖的原則:
- 建立合適的索引,減小鎖的粒度
- 選擇合適的事務隔離級別
- 大事務拆成小事務,一個事務中的鎖盡量少
總結
以上是生活随笔為你收集整理的mysql死锁场景汇总整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 熟读《阿里巴巴java开发手册》(六、工
- 下一篇: java中==和equals引发的思考