Lock wait timeout exceeded; try restarting transaction
Lock wait timeout exceeded; try restarting transaction
这个错误通常出现在 MySQL 数据库,表示事务等待获取锁的时间超过了 innodb_lock_wait_timeout
配置的超时时间。可能的原因包括:
可能的原因
-
长时间未提交的事务
- 其他事务持有锁,但未提交或回滚,导致当前事务超时。
-
死锁(Deadlock)
- 两个或多个事务互相等待对方释放锁,导致锁等待超时。
-
锁冲突(Lock Contention)
- 多个事务频繁更新相同的行或表,导致锁资源争夺。
-
大范围锁定
UPDATE
、DELETE
影响大量行时,可能会锁表或索引范围,导致其他事务等待。
解决方案
1. 查找占用锁的事务
运行以下 SQL 查询,找出正在持有锁的事务:
SELECT * FROM information_schema.INNODB_TRX;
然后找到 trx_state = 'LOCK WAIT'
的事务,并查看 trx_mysql_thread_id
,再杀掉该事务:
KILL <thread_id>;
2. 查找死锁
如果是死锁问题,可以使用:
SHOW ENGINE INNODB STATUS;
如果发现 LATEST DETECTED DEADLOCK
,需要优化事务顺序,避免循环依赖。
3. 优化事务
- 减少锁持有时间:
- 确保事务代码逻辑尽量短,及时
COMMIT
或ROLLBACK
。
- 确保事务代码逻辑尽量短,及时
- 使用更细粒度的锁:
SELECT ... FOR UPDATE
只锁定查询的行,而不是整张表。
- 调整 SQL 顺序:
- 避免多个事务以不同顺序操作相同的资源,降低死锁概率。
4. 调整 innodb_lock_wait_timeout
如果事务确实需要较长时间,可以增加超时时间(默认 50 秒):
SET innodb_lock_wait_timeout = 120;
或在 my.cnf
配置:
[mysqld]
innodb_lock_wait_timeout=120
但这只是临时缓解,根本问题还是优化 SQL 和事务设计。
5. 解决锁争用
如果是高并发导致的锁冲突,可以:
- 索引优化:确保
WHERE
条件命中索引,避免锁全表。 - 分批更新:
UPDATE table_name SET col = value WHERE id BETWEEN 1000 AND 2000;
- 使用乐观锁(Optimistic Locking):
通过UPDATE table_name SET col = value, version = version + 1 WHERE id = ? AND version = ?;
version
解决并发更新问题。
总结
- 检查未提交的事务 (
SHOW ENGINE INNODB STATUS
) - 优化 SQL 语句,避免锁表
- 使用索引,减少锁范围
- 使用批量更新,避免一次性锁住大量行
- 调整
innodb_lock_wait_timeout
作为临时缓解
你的场景是在哪种操作下发生的?是 UPDATE
、DELETE
还是 SELECT ... FOR UPDATE
?