mysql锁等待本质是事务并发访问资源时协调失效,需厘清“谁在等、等什么、为什么等不住”;须区分行锁(索引查询)、间隙锁(rr级范围查询防幻读)、临键锁(行锁+间隙锁)、表锁(ddl或全表扫描触发)。

MySQL 锁等待问题本质是事务并发访问同一资源时的协调机制失效,核心要抓住“谁在等、等什么、为什么等不住”三个关键点。
锁类型与触发场景必须分清
面试中常混淆行锁、间隙锁、临键锁和表锁的适用条件:
- 行锁(Record Lock):只作用于索引记录本身,前提是查询走索引(主键或二级索引),否则会升级为表锁;
-
间隙锁(Gap Lock):锁定索引记录之间的空隙,防止幻读,仅在 RR 隔离级别下生效,且只对范围查询(如
WHERE id BETWEEN 10 AND 20)或SELECT ... FOR UPDATE等显式加锁语句起作用; - 临键锁(Next-Key Lock) = 行锁 + 间隙锁,是 RR 级别默认的行级锁机制,用于解决幻读;
-
表锁(Table Lock):如
LOCK TABLES、DDL 操作(ALTER TABLE)、或未走索引的UPDATE/DELETE会触发全表扫描并加意向锁,进而阻塞其他 DML。
如何快速定位锁等待链
重点掌握两个系统视图的组合使用:
-
SELECT * FROM performance_schema.data_lock_waits;—— 直接看到哪个事务在等哪个锁(8.0.17+); -
SELECT * FROM information_schema.INNODB_TRX;查运行中事务,结合trx_state = 'LOCK WAIT'找出被阻塞者; -
SELECT * FROM information_schema.INNODB_LOCKS;(5.7)或performance_schema.data_locks;(8.0)看具体锁对象; -
SELECT * FROM information_schema.INNODB_LOCK_WAITS;关联锁等待关系(注意:8.0 已废弃,改用data_lock_waits)。
典型输出中关注:WAITING_TRX_ID 和 BLOCKING_TRX_ID,再回查 INNODB_TRX 中对应事务的 trx_mysql_thread_id,就能用 SHOW PROCESSLIST 看到 SQL 和状态。
常见“假死锁”与设计陷阱
很多看似死锁实为单向等待,根源常在应用层逻辑:
-
非唯一索引更新引发多行锁:例如
UPDATE t SET c=1 WHERE name='Alice';,若name无唯一约束,可能命中多条记录,加多个行锁,又与其他事务交叉更新,极易形成锁等待; - 长事务持有锁不释放:事务里混入慢查询、网络调用、循环处理等,导致锁占用时间远超预期;
- 不按固定顺序加锁:两个事务分别先更新 A 再更新 B、和先更新 B 再更新 A,虽非严格死锁,但在高并发下大概率相互阻塞;
- 隐式锁升级:如 WHERE 条件没走索引,InnoDB 无法精确加行锁,退化为聚簇索引全扫描 + 意向锁,实际效果接近表级阻塞。
优化与规避思路要落地
不能只说“加索引”“缩短事务”,得讲清怎么做:
- 写操作尽量走唯一索引或主键,避免范围扫描;
- 批量更新拆成小批量(如每次 100 行),配合
COMMIT释放锁; - 业务上允许时,把
SELECT ... FOR UPDATE提前到事务开头,并确保后续逻辑不会长时间空转; - 监控层面接入
innodb_row_lock_time_avg和innodb_row_lock_waits状态变量,设置阈值告警; - 必要时降级隔离级别(如 RC),放弃间隙锁,但需评估幻读风险是否可接受。
不复杂但容易忽略。










