锁等待超时不一定是死锁,需先执行SHOW ENGINE INNODB STATUS\G确认是否存在“LATEST DETECTED DEADLOCK”段落;若无,则为普通锁等待,应查sys.innodb_lock_waits定位阻塞SQL。

锁等待超时是死锁吗?先用一条命令确认
不是所有 Lock wait timeout exceeded 都是死锁。盲目查死锁日志会浪费时间。直接执行:SHOW ENGINE INNODB STATUS\G
看输出里有没有 LATEST DETECTED DEADLOCK 段落。有,才是死锁;没有,就是普通锁等待——说明某个事务卡住没释放锁,别人在排队等它。
更轻量的验证方式:SELECT * FROM sys.innodb_lock_waits\G
如果有结果,blocking_query 字段就是真正在阻塞别人的 SQL;为空或无返回,大概率锁已释放,或者问题已过去。
怎么立刻找到“真凶”事务?查三张表组合
别只看 SHOW PROCESSLIST,它看不到锁等待关系。必须用 INFORMATION_SCHEMA 的实时快照:
-
INNODB_TRX:列出所有活跃事务,重点关注TRX_STARTED时间和TRX_STATE(RUNNING才是持锁者,LOCK WAIT是等锁方) -
INNODB_LOCK_WAITS:连接等锁方和持锁方的“阻塞链”,关键字段是BLOCKING_TRX_ID和REQUESTING_TRX_ID -
INNODB_LOCKS:确认锁类型(行锁还是间隙锁)、锁在哪一行(LOCK_TRX_ID+LOCK_INDEX+LOCK_DATA)
常用组合查询(复制即用):
SELECT t1.TRX_ID waiting_trx_id, t1.TRX_MYSQL_THREAD_ID waiting_thread, t1.TRX_QUERY waiting_query, t2.TRX_ID blocking_trx_id, t2.TRX_MYSQL_THREAD_ID blocking_thread, t2.TRX_QUERY blocking_query FROM INFORMATION_SCHEMA.INNODB_TRX t1 INNER JOIN INFORMATION_SCHEMA.INNODB_LOCK_WAITS w ON t1.TRX_ID = w.REQUESTING_TRX_ID INNER JOIN INFORMATION_SCHEMA.INNODB_TRX t2 ON w.BLOCKING_TRX_ID = t2.TRX_ID;
注意:t2 是持锁事务,KILL 它才能解围;KILL t1 没用,锁还在。
为什么加了索引还锁表?行锁退化的真实条件
很多人以为“加了索引就一定走行锁”,但实际中常因以下原因退化成表锁或间隙锁:
- WHERE 条件没走索引(比如隐式类型转换:
id = '1'对 int 字段)→ 触发全表扫描 → 行锁升级为表锁 - UPDATE/DELETE 语句没带 WHERE,或 WHERE 匹配太多行 → InnoDB 可能放弃行锁,直接锁整个索引范围
- RR 隔离级别下,即使走索引,也会额外加间隙锁(
LOCK_MODE显示X,GAP),两个事务对同一间隙加锁不冲突,但 INSERT 就会被卡住
验证是否走索引:EXPLAIN SELECT ... 或 EXPLAIN UPDATE ...,重点看 type(应为 const/ref)、key(是否用了预期索引)、rows(扫描行数是否合理)。
KILL 之前必须确认的三件事
别一看到 long-running 就 KILL,容易引发数据不一致:
- 确认
t2.TRX_STATE是RUNNING,不是ROLLING BACK或已断开(TRX_MYSQL_THREAD_ID在PROCESSLIST中查不到) - 检查该事务是否在执行关键业务逻辑(比如支付扣款、库存预占),
TRX_QUERY是否包含敏感操作 - 确认应用层是否有重试机制——KILL 后若立即重试,可能再次撞上同一锁点
真正要优先处理的,是那些 TRX_STARTED 超过 5 分钟、且 TRX_QUERY 为空或明显卡在非 SQL 环节(如网络等待、应用层 sleep)的事务。


















