一次死锁的排查过程
# 一次死锁的排查过程
数据库报 Deadlock found,业务偶发失败。死锁不难解决,难的是定位是哪两个事务在互相等。这篇记一次实际排查。
## 现象
订单创建偶发失败,日志里有 `Deadlock found when trying to get lock`。不是必现,高峰期更容易出现。
## 第一步:看死锁日志
MySQL 兼容库有 `SHOW ENGINE INNODB STATUS`,里面有一段 LATEST DETECTED DEADLOCK,记录最近一次死锁的两个事务和它们持有的锁。
```sql
SHOW ENGINE INNODB STATUS\G
```
重点看:
- 两个事务分别执行了什么 SQL
- 各自持有什么锁、在等什么锁
## 第二步:还原场景
那次日志显示:
- 事务 A:先更新订单表,再更新库存表
- 事务 B:先更新库存表,再更新订单表
两个事务加锁顺序相反,交叉等待,形成死锁。
## 第三步:解决
**统一加锁顺序**:所有涉及这两张表的事务,都按「订单 → 库存」的顺序加锁。顺序一致就不会交叉。
**缩小事务范围**:事务里别做耗时操作,尽快提交,减少持锁时间。
**降低隔离级别**:如果业务允许,用 READ COMMITTED 减少间隙锁。
**加索引减少锁范围**:没有索引的更新会锁更多行,容易冲突。
## 预防措施
- 约定资源加锁顺序,写进开发规范
- 事务尽量短
- 更新条件走索引
- 高频冲突的表考虑拆分
## 死锁不是 bug
要理解:死锁是数据库的一种自我保护,检测到就回滚一个事务。应用层遇到死锁应该重试,而不是当成致命错误。
```python
for attempt in range(3):
try:
do_transaction()
break
except DeadlockError:
time.sleep(0.1 * (attempt + 1))
```
## 小结
死锁排查靠死锁日志,解决靠统一加锁顺序和缩短事务。应用层要有重试机制。死锁本身不可怕,可怕的是不知道为什么会死锁。把加锁顺序规范起来,能消除大部分死锁。
- 点赞
- 收藏
- 关注作者
评论(0)