朕喜技术(ZhenxiJS)
2026.08.30
1 分钟阅读
MySQL 死锁(Deadlock found when trying to get lock)根因与解决实战
问题背景
电商下单系统在并发事务下间歇性报错:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
服务端日志持续出现该错误,部分订单提交失败,但数据未丢失。业务逻辑:下单时先更新 orders 订单表,再扣减 inventory 库存表。
复现步骤
环境:Docker + MySQL 8.0 + Python pymysql
cd scenario/2026-08-30-0000
docker compose up -d --build
docker compose exec app python3 scripts/deadlock_demo.py
复现脚本模拟两个并发事务以不同顺序加锁:
[事务A] 先锁 orders.id=1 → 再锁 inventory.product_id=101
[事务B] 先锁 inventory.product_id=101 → 再锁 orders.id=1
预期关键输出:
[事务A] 死锁被检测到!错误: (1213, 'Deadlock found when trying to get lock; try restarting transaction')
[事务A] 已回滚
[事务B] 提交成功
查看死锁详情:
docker compose exec mysql mysql -uroot -proot123 -e "SHOW ENGINE INNODB STATUS\G"
根本原因
两个并发事务对同一组行以不同顺序加锁,形成循环等待(死锁):
- 事务A:锁住
orders.id=1,等待inventory.product_id=101 - 事务B:锁住
inventory.product_id=101,等待orders.id=1 - A 等 B 的锁,B 等 A 的锁 → 永不释放
InnoDB 检测到死锁后,自动回滚其中一个事务(通常选较小的事务),并返回错误码 1213。InnoDB 的 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 段会显示两个事务分别 HOLDS 什么锁、WAITING FOR 什么锁,是定位根因的关键工具。
解决办法
统一加锁顺序:让所有事务按固定顺序加锁(如都「先锁 inventory 再锁 orders」)。
这样即使两个事务并发,也只会有一个在等锁(正常排队),不会形成循环等待,最终都能提交。
# 验证脚本 solution_demo.py 中两个事务均改为先锁 inventory 再锁 orders
docker compose exec app python3 scripts/solution_demo.py
输出:
[事务A-修复] 提交成功 ✅
[事务B-修复] 提交成功 ✅
连续运行多次均成功,无死锁,数据扣减正确(无超卖/漏扣)。
其他可选方案
- 统一加锁顺序(推荐,最简单可靠)
SELECT ... FOR UPDATE NOWAIT:拿不到锁立即返回,避免长时间等待- 调小
innodb_lock_wait_timeout:缩短锁等待超时 - 应用层重试:捕获
1213死锁错误后自动重试该事务
经验与避坑
- 死锁 ≠ 数据损坏:InnoDB 会回滚其中一个冲突事务,数据不会丢,但业务会失败,必须配合重试机制保证最终一致。
- 多表/多行更新的加锁顺序必须全局统一:这是避免死锁最根本的手段,应在代码规范里强制约束。
- 定位死锁用
SHOW ENGINE INNODB STATUS:重点看LATEST DETECTED DEADLOCK段的 HOLDS THE LOCK / WAITING FOR THIS LOCK。 - 死锁错误码:
1213 (40001),应用层要捕获并重试。 - Docker 环境踩坑:
docker-compose.yml中dockerfile路径是相对 build context 解析的,不是相对 compose 文件;Debian 12 的 Python 用 pip 需加--break-system-packages。
可复现命令速查
docker compose up -d --build
docker compose exec app python3 scripts/deadlock_demo.py # 复现死锁
docker compose exec mysql mysql -uroot -proot123 -e "SHOW ENGINE INNODB STATUS\G" # 看死锁详情
docker compose exec app python3 scripts/solution_demo.py # 验证解决方案
技术栈:MySQL 8.0 / InnoDB / 事务 / 行锁 / pymysql / Docker Compose
