MySQL锁等待会显著拖慢查询响应时间,表现为SHOW PROCESSLIST中大量Waiting for table metadata lock或row lock状态,执行时间从毫秒级升至秒级甚至超时。

锁等待会直接拖慢查询响应时间
是的,MySQL 锁(尤其是行锁升级为表锁、或大量事务争抢同一行)会导致明显延迟。典型表现是 SHOW PROCESSLIST 中出现大量 Waiting for table metadata lock 或 Waiting for row lock 状态,对应 SQL 执行时间从毫秒级跳到秒级甚至超时。
实操建议:
- 用
SELECT * FROM performance_schema.data_locks查看当前持有的锁(MySQL 8.0+),注意LOCK_TRX_ID和LOCK_DATA字段定位冲突行 - 避免在事务中执行耗时操作(如调用外部 API、大字段处理),否则锁持有时间被拉长
- 写操作尽量走索引——全表扫描更新会升级为表级意向锁,连带阻塞其他 DML
InnoDB 行锁不是万能的,主键和二级索引影响锁范围
很多人以为“用了 InnoDB 就自动行锁”,但实际锁行为高度依赖 WHERE 条件是否命中索引、以及索引类型。比如 UPDATE t SET x=1 WHERE name='a',若 name 没有索引,就会锁全表;若只有普通二级索引,InnoDB 还要回表加锁,可能意外锁住相邻主键记录(间隙锁)。
实操建议:
- 用
EXPLAIN确认 UPDATE/DELETE 的type是const或range,而非ALL - 对高频更新字段建联合索引,避免因索引失效导致锁扩大
- 业务允许时,用主键 ID 更新代替业务字段更新,锁更精准、无间隙锁风险
长事务是锁问题的放大器
事务开启后未提交,它持有的所有锁都不会释放,哪怕只执行了一条 SELECT ... FOR UPDATE。这时后续任何想修改这些行的语句都会卡住,还可能引发 Lock wait timeout exceeded 错误。
实操建议:
- 监控
information_schema.INNODB_TRX表,重点关注TRX_STARTED和TRX_STATE,及时 kill 掉运行超过 30 秒的ACTIVE事务 - 应用层设置事务超时(如 Spring 的
@Transactional(timeout = 5)),防止异常挂起 - 避免在事务里做日志打印、Redis 写入等非 DB 操作——它们不参与事务,但会延长事务生命周期
MDL 锁(元数据锁)容易被忽视但杀伤力强
只要一个连接在查某张表,另一个连接就无法对该表执行 ALTER TABLE、DROP INDEX 等 DDL。更隐蔽的是:一个慢查询正在执行 SELECT,也会让后续 DDL 卡在 Waiting for table metadata lock,且这个锁不会出现在 data_locks 里。
实操建议:
- DDL 操作务必避开业务高峰,并提前检查是否有长查询:用
SELECT * FROM information_schema.PROCESSLIST WHERE INFO LIKE '%your_table_name%' AND COMMAND != 'Sleep' - MySQL 5.7+ 可设
lock_wait_timeout=10(会话级),让 DDL 失败快于无限等待 - 线上禁止直接
ALTER TABLE大表,改用pt-online-schema-change或 MySQL 8.0 的原地 DDL
锁问题从来不是孤立的——它总和索引设计、事务边界、SQL 写法、甚至应用重试逻辑耦合在一起。最常被忽略的一点是:开发环境没并发,测试压测又只看吞吐,结果上线后锁冲突才突然爆发。真要排查,得把 performance_schema 当日常工具用,而不是出事了再翻文档。











