MySQL 8.0+ 应使用 performance_schema.data_locks 和 data_lock_waits 查锁及等待关系,结合 INFORMATION_SCHEMA.INNODB_TRX 定位长事务,避免全表扫描加锁,统一DML顺序减少死锁。

查当前被锁住的事务和行记录
InnoDB 行锁本身不直接暴露“谁锁了哪一行”,得靠 INFORMATION_SCHEMA.INNODB_TRX、INFORMATION_SCHEMA.INNODB_LOCKS(MySQL 5.7 及以前)或更可靠的 performance_schema.data_locks(MySQL 8.0+)交叉分析。别只看 SHOW ENGINE INNODB STATUS,它只保留最近一次死锁信息,且输出杂乱。
实操建议:
- MySQL 8.0+ 推荐用:
SELECT * FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
- 配合
performance_schema.data_lock_waits查等待关系,能直接看到BLOCKING_TRX_ID和REQUESTING_TRX_ID - 注意
LOCK_DATA字段:显示被锁的具体主键值(如123),但仅当锁在聚集索引上且能解析时才非 NULL;二级索引锁可能显示索引字段值,也可能为空 - 避免在高并发下频繁查
data_locks,它有性能开销,临时排查用即可
识别“锁住了却没提交”的长事务
最常见卡顿源头不是 SQL 写得差,而是应用端开启事务后忘了 COMMIT 或 ROLLBACK,导致行锁一直挂着。这类事务在 INFORMATION_SCHEMA.INNODB_TRX 里表现为 TRX_STATE = 'RUNNING' 但 TRX_STARTED 时间异常久。
实操建议:
- 快速定位:
SELECT TRX_ID, TRX_MYSQL_THREAD_ID, TRX_QUERY, TRX_STARTED FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_STATE = 'RUNNING' AND TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) > 60;
-
TRX_MYSQL_THREAD_ID可用来 kill:KILL [thread_id],但先确认是否真可中断(比如是不是正在跑批处理) - 应用层务必设置
innodb_lock_wait_timeout(默认 50 秒),并捕获Lock wait timeout exceeded错误,而不是静默重试 - ORM 框架(如 Django、MyBatis)要注意事务传播行为,
@Transactional嵌套或未标注方法可能意外延长事务生命周期
UPDATE/DELETE 语句没走索引导致全表扫描加锁
这是行锁变表锁的隐形开关。只要 WHERE 条件没命中索引,InnoDB 就会在所有扫描过的行上加临键锁(next-key lock),哪怕最终只改 1 行,也可能锁住几万行。
实操建议:
- 执行前一定
EXPLAIN看type是否为range/ref,避开ALL或index - 特别警惕隐式类型转换:比如
WHERE user_id = '123'(字段是INT),会丢索引,进而扩大锁范围 - 用
SELECT ... FOR UPDATE测试锁范围:先执行它,再开另一个会话尝试更新同一张表的其他行,看是否被阻塞——这是验证是否“误锁”的最快方式 - 批量更新尽量按主键分片,避免单条语句扫描过大范围
死锁日志里怎么看谁抢了哪把锁
SHOW ENGINE INNODB STATUS 输出里的 LATEST DETECTED DEADLOCK 区块,关键不是看“Transaction X rolled back”,而是看两段 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: 和 *** (2) HOLDS THE LOCK(S): 的对比。
实操建议:
- 重点比对两处的
lock_mode(如X locks rec but not gap)、lock_trx_id、lock_rec_primary_key(主键值)和lock_index(哪个索引) - 如果 A 在等 B 持有的某行 X 锁,而 B 又在等 A 持有的另一行 X 锁,就构成环——但实际日志里常出现“B 等 A 的间隙锁,A 等 B 的记录锁”,说明索引设计或查询条件覆盖不全
- 别忽略
mysql tables in use行,它提示是否涉及外键或触发器,这些会额外加锁 - 死锁不是错误,是正常并发现象;重点是减少频率——手段包括统一 DML 顺序(如总是先更新用户表再更新订单表)、缩短事务时间、拆大事务为小事务
真正难的不是查到锁,而是判断“这个锁该不该存在”:它到底是业务逻辑必须的互斥,还是索引缺失、事务粒度失控、或者应用层没正确释放连接导致的副作用。监控可以告警,但根因得回到代码里一行行对齐。










