确认事务真在等锁需查innodb_trx中trx_state='lock wait',若全为'running'或'active'则卡在外部调用、慢查询等非锁环节;有结果再结合innodb_lock_waits和processlist定位阻塞源。

怎么确认事务真在等锁,而不是卡在别处
别一看到 Lock wait timeout exceeded 就默认是锁问题——它只是表象。真正卡在锁上的事务,在 INNODB_TRX 里 trx_state 一定是 'LOCK WAIT';如果查出来全是 'RUNNING' 或 'ACTIVE',那大概率是事务本身卡在外部调用、慢查询、磁盘 IO 或网络延迟上。
实操建议:
- 先跑
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'LOCK WAIT',没结果就别往下查锁了 - 有结果时,记下
trx_mysql_thread_id和trx_started,对比当前时间看是否真卡住(比如差了 8 秒但还没结束) - 再查
INNODB_LOCK_WAITS,只在有等待关系时才有数据;空结果说明不是行锁/间隙锁堵的 - 如果
PROCESSLIST里对应线程STATE是'Waiting for table metadata lock',那是 DDL 在抢 MDL 锁,跟行锁无关,得去performance_schema.metadata_locks查
UPDATE/DELETE 没走索引,锁就炸了
这是最隐蔽也最常踩的坑:语句看着简单,EXPLAIN 却显示 type = 'ALL',意味着全表扫描 → 每扫一行加一个 X 锁 → 锁成千上万行 → 别的事务一碰就等。
典型场景:
-
UPDATE user SET status = 1 WHERE phone = '138...',但phone字段没索引 DELETE FROM log WHERE create_time ,<code>create_time无索引或索引失效(如用了函数:DATE(create_time))- 联合索引字段顺序错,比如索引是
(a, b),却查WHERE b = ?
查法很简单:EXPLAIN FORMAT=TRADITIONAL your_sql,重点盯 type、key、rows 三列。别信“执行快就没问题”——快可能是缓存,锁是实打实加在每行上的。
事务里混了 sleep()、HTTP 调用、文件读写
InnoDB 行锁是事务级的,只要事务没提交,锁就一直挂着。而事务里一旦掺了非数据库操作,锁就变成“悬停状态”——数据库干等着你回来 commit,别的事务只能排队。
识别方式很直接:
-
INNODB_TRX里trx_started时间远早于当前时间(比如差 15 秒),但trx_state还是'RUNNING' - 对应线程在
PROCESSLIST中STATE是'Sleep'或'Sending data'却迟迟不结束 - 应用日志里能看到 RPC 超时、HTTP 504、本地文件读取卡顿等痕迹
这类问题不能靠调 innodb_lock_wait_timeout 解决——参数只管“等多久放弃”,不管“为什么等”。必须把耗时操作移出事务,或者拆成多个小事务。
批量 INSERT 撞上自增锁,高并发下排队明显
MySQL 5.7+ 默认 innodb_autoinc_lock_mode = 1(连续模式),整条 INSERT ... SELECT 或大批量 INSERT 执行完才释放自增锁。并发高时,后到的 INSERT 就在队列里干等。
验证方法:
-
SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode',值为 1 就是默认模式 - 查
INNODB_TRX,发现多个事务都在等同一张表的自增字段,且trx_query都是 INSERT - 现象:单条 INSERT 很快,但并发 20 个一起发,响应时间呈线性增长
临时缓解可设为 2(交错模式):SET GLOBAL innodb_autoinc_lock_mode = 2,但注意:该模式要求 binlog_format = ROW,且重启后生效;已连的老连接不会变,这点极易漏掉。
锁等待不是配置问题,是代码和 SQL 的执行路径问题。查不到阻塞源头,所有调参都只是把失败从 5 秒延后到 30 秒,而连接池可能先爆掉。











