SQL锁等待过长主因是事务未及时释放锁,需通过监控定位阻塞源头(如查innodb_trx、关联锁等待表)与调参(如调大innodb_lock_wait_timeout、开死锁日志)双管齐下,并从代码优化事务边界、索引优化、分批操作等源头减少锁压力,同时建立常态化监控预警机制。

SQL锁等待时间过长,核心是事务没及时释放锁,其他操作只能排队干等,直到超时报错。解决这事不靠猜,得靠监控定位+策略控制双管齐下。
实时查谁在卡住别人
一发现超时,立刻查阻塞源头:
- 运行 SELECT * FROM information_schema.innodb_trx,重点关注 trx_started 时间早于当前几分钟的事务,它们大概率是“钉子户”
- 用关联查询揪出具体阻塞关系:
SELECT r.trx_mysql_thread_id AS waiting_thread, b.trx_mysql_thread_id AS blocking_thread, r.trx_query AS waiting_sql, b.trx_query AS blocking_sql
FROM information_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id; - 配合 SHOW PROCESSLIST 看线程状态,标记为 Locked 或 Waiting for table metadata lock 的要重点盯
调参不是万能,但默认值常不够用
innodb_lock_wait_timeout 默认 50 秒,在复杂业务里真不够用:
- 临时调整(当前会话):SET SESSION innodb_lock_wait_timeout = 120;
- 全局调整(需重启或动态生效,视版本而定):SET GLOBAL innodb_lock_wait_timeout = 120;
- 别忘了打开死锁记录:SET GLOBAL innodb_print_all_deadlocks = ON;,错误日志里就能看到完整死锁链
从代码和SQL本身减少锁压力
监控和调参是救火,真正治本得从源头压锁时长和范围:
- 事务边界要精准:Spring 的 @Transactional 别套整个方法,只包真正需要数据库一致性的逻辑段
- 避免隐式长事务:自动提交关了?检查有没有漏掉 COMMIT 或 ROLLBACK 的分支
- 查数据尽量走索引:EXPLAIN 看执行计划,type 不要是 ALL,rows 别动辄上百万;间隙锁在 RR 级别很活跃,用 READ COMMITTED 可减少这类锁
- 批量更新/删除分批次做:一次改 10 万行不如拆成 1000 行一批,锁持有时间大幅下降
建立常态化监控防线
等报警响再动手,已经晚了。把关键指标纳入日常盯盘:
- 定时采集 information_schema.innodb_trx 中事务运行时长,超过 60 秒就告警
- 监控 performance_schema.events_waits_summary_global_by_event_name 里的 lock wait 次数突增
- 结合缓冲池命中率、锁等待队列长度(>10 就该警惕),综合判断是不是锁问题开始蔓延










