lock wait timeout exceeded 表示事务等待锁超时而非数据库宕机,常见于 update、delete 或 select ... for update;需查 information_schema.innodb_trx 和 innodb_lock_waits 定位阻塞源,根因多为长事务未提交、sql 未走索引或事务内混入耗时操作。

MySQL锁等待超时错误怎么看
Lock wait timeout exceeded 不是数据库挂了,而是某个事务卡在等锁,等太久被强制中断。常见于 UPDATE、DELETE 或带 FOR UPDATE 的 SELECT 操作。错误里通常还跟着具体表名和事务ID,比如:Lock wait timeout exceeded; try restarting transaction。
- 错误本身不说明谁在锁,只说明“我等不及了”
- 默认超时时间由
innodb_lock_wait_timeout控制(通常 50 秒),改大只是掩耳盗铃,不能根治 - 真正要查的是:谁拿了锁没释放?谁在等这个锁?
查正在阻塞的事务和锁
用 INFORMATION_SCHEMA.INNODB_TRX 和 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 联查最直接:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM INFORMATION_SCHEMA.INNODB_TRX r INNER JOIN INFORMATION_SCHEMA.INNODB_LOCK_WAITS w ON r.trx_id = w.requesting_trx_id INNER JOIN INFORMATION_SCHEMA.INNODB_TRX b ON b.trx_id = w.blocking_trx_id;
- 如果查不到结果,可能是锁已释放,或当前没有活跃的锁等待
-
trx_state = 'LOCK WAIT'的事务才是真正在等锁的 - 注意
trx_started时间,如果一个事务运行了几分钟还没提交,大概率就是它在 hold 锁
常见锁等待场景和修复动作
- 长事务没提交:应用代码里开了事务但忘记
COMMIT 或 ROLLBACK,尤其在异常分支里遗漏
- 自动提交关了(
autocommit=0)后执行了 DML 却没手动提交,连接空闲着但锁一直占着
- 应用重试逻辑不当:比如失败后立刻重试,反而让多个请求排队等同一行锁
- SQL 没走索引:
UPDATE user SET status=1 WHERE name='xxx',若 name 无索引,会升级为表级锁或大量行锁,加剧冲突
- 使用
SELECT ... FOR UPDATE 范围过大,或在事务里做耗时操作(如调外部 API、发邮件)后再更新
COMMIT 或 ROLLBACK,尤其在异常分支里遗漏 autocommit=0)后执行了 DML 却没手动提交,连接空闲着但锁一直占着 UPDATE user SET status=1 WHERE name='xxx',若 name 无索引,会升级为表级锁或大量行锁,加剧冲突 SELECT ... FOR UPDATE 范围过大,或在事务里做耗时操作(如调外部 API、发邮件)后再更新 杀掉阻塞源事务可用:KILL [thread_id],但得先确认它不是关键业务流程。
预防比排查更重要
锁等待本质是资源争用,优化方向很明确:减少锁持有时间、缩小锁粒度、避免不必要的锁。
- 所有写操作尽量短平快,事务里别混业务逻辑
- 写前先
SELECT ... FOR UPDATE?不如用唯一键INSERT ... ON DUPLICATE KEY UPDATE或REPLACE INTO,原子性更强 - 检查慢查询日志,
SELECT扫描行数大的语句往往也意味着UPDATE/DELETE会锁更多行 - 开发环境打开
innodb_print_all_deadlocks=ON,配合错误日志看死锁链路 - 监控
Innodb_row_lock_waits和Innodb_row_lock_time_avg这两个状态变量,有持续上涨就得查
锁等待问题很少孤立出现,背后常连着事务设计缺陷或索引缺失。定位到具体 SQL 后,别只盯着加索引,先问一句:这事务真的需要跨多个语句、保持这么长时间吗?










