先查innodb_trx中trx_state='lock wait'的等待事务,再通过innodb_lock_waits定位blocking_trx_id,最后用processlist查该id对应线程的info字段sql即为阻塞源头。

怎么快速定位谁在持有行锁
别只看 SHOW PROCESSLIST,它不显示锁关系,也看不出哪个事务真正在“堵路”。真正能定位到源头的,是三张系统表联动查:
- 先查等待中的事务:
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'LOCK WAIT',重点关注trx_started(等待开始时间)和trx_mysql_thread_id - 再查阻塞链:
SELECT * FROM information_schema.INNODB_LOCK_WAITS,拿到blocking_trx_id(肇事者事务ID)和requested_lock_id(受害者请求的锁) - 最后关联线程:
SELECT ID, USER, HOST, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE ID = ?,把blocking_trx_id转成线程 ID 去查真实 SQL
如果 STATE 是 Waiting for table metadata lock,说明不是行锁问题,而是 DDL(比如 ALTER TABLE)卡在元数据锁上,得换路径查 performance_schema.metadata_locks。
UPDATE/DELETE 为什么总卡住
常见硬伤就三个,每种都有明确现场特征,一查一个准:
-
EXPLAIN看执行计划,type是ALL→ 没走索引 → 全表扫描 → 锁成千上万行 → 别的事务一碰就等 -
INNODB_TRX里trx_started和当前时间差超过 5 秒 → 事务里混了 RPC、HTTP 请求、文件读写或sleep()→ 事务挂起,锁一直不放 - 高并发批量
INSERT→ 查SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode',MySQL 5.7 默认为 1(语句级自增锁),整条语句执行完才释放;设为 2(轻量级模式)可缓解,但需重启生效
innodb_lock_wait_timeout 设多少才合理
这个参数只控制“等多久就放弃”,不解决“为什么等”。设太小(如 1 秒)会误杀正常慢事务;设太大(如 300 秒)用户卡五分钟才失败,体验更差:
- 核心接口建议设为
5或10:失败快,前端能及时降级或重试 - 后台批处理可设
120,但前提是事务本身不能真跑两分钟——比如避免在事务里调外部接口 -
SET innodb_lock_wait_timeout = 10只对当前会话生效;SET GLOBAL对新连接生效,已连的老连接不会变,这点极易踩坑
最容易被忽略的锁类型陷阱
很多人以为“加了索引就一定是行锁”,其实不是。InnoDB 的锁行为高度依赖隔离级别和查询条件:
- 在
REPEATABLE-READ下,WHERE id > 1 AND id 这类范围查询会触发 <code>Next-Key Lock(记录锁 + 间隙锁),锁住整个区间,不只是命中行 -
SELECT ... FOR UPDATE或UPDATE没走索引时,会退化成表级锁(本质是给每条聚集索引记录加 X 锁),整张表被堵死 - 唯一索引 + 精准匹配(如
WHERE id = 1)才可能只加Record Lock;非唯一索引 + 精准匹配,仍可能加间隙锁
查锁细节必须用 SHOW ENGINE INNODB STATUS\G,重点看 LATEST DETECTED DEADLOCK 或 TRANSACTIONS 部分的 lock_mode 和 lock_type 字段,光看 SQL 写法容易误判。











