MySQL 报错 Deadlock found when trying to get lock 的原因与解决办法
MySQL 报错 Deadlock found when trying to get lock 的原因与解决办法
在 MySQL 使用过程中有时候会遇到一个简单却需要精精校核的报错:
1
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
通常会出现在多个连接/事务并发执行定向 SQL 操作时,特别是 DELETE/UPDATE 形式的文件表操作。
1. 基本原因
当多个事务同时试图锁定多个记录,但锁的顺序不同时,可能导致相互占用(比如 A 锁了记录 1,等待记录 2;B 锁了记录 2,等待记录 1),则系统无法解决这种相互等待,起用「逻辑分析」判定出现死锁,其中一个事务被终止。
2. 实际经验:DELETE 操作使用非主键导致死锁的错误样式
1
DELETE FROM onlineusers WHERE datetime <= NOW() - INTERVAL 900 SECOND;
上面这条 DELETE 语句使用 datetime 字段,如果此列未给主键或非唯一索引,在这种表形上就有可能导致多线程报 deadlock 错误。
3. 推荐写法:先查出 id ,再根据id DELETE
1
2
3
4
5
6
7
DELETE FROM onlineusers WHERE id IN (
SELECT id FROM (
SELECT id FROM onlineusers
WHERE datetime <= NOW() - INTERVAL 900 SECOND
ORDER BY id
) AS sub
);
说明:不能直接在 IN (子查询)中重复同表操作,必须内外子查询分隔 (MySQL 限制)
4. 预防策略
✅ 保证多个连接的操作顺序相同
如:
- connection 1: locks id(1), locks id(2)
- connection 2: locks id(2), locks id(1) ❌ 可能死锁
- connection 2: locks id(1), locks id(2) ✅ 同序无死锁
✅ 操作时使用主键/唯一索引
避免表的多次全表扫描,更快排序和锁约。
✅ 加入客户端重试逻辑
MySQL 官方文档建议:
“If a deadlock occurs, retry the transaction.”
建议在端程序加入自动重试逻辑,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int retryCount = 0;
while (retryCount < 3)
{
try
{
// 执行操作
break;
}
catch (MySqlException ex) when (ex.Message.Contains("Deadlock found"))
{
retryCount++;
Thread.Sleep(100); // 等等线程重新分配
}
}
5. 总结
死锁很常见于多事务并发时间的执行顺序不一致,特别是启用 InnoDB 库时。
DELETE/UPDATE 操作应使用主键或唯一索引来减少问题。
建议尽可能接入 “顺序锁” + “回退重试”策略。
本文由作者按照 CC BY 4.0 进行授权