explain显示type: all即未走索引,innodb将全表扫描并逐行加锁,效果等同表锁;根本原因是where条件未命中索引,如隐式转换、like前缀通配、联合索引不满足最左前缀等。

MySQL 从不主动“升级”锁——所谓行锁变表锁,本质是优化器放弃索引后被迫逐行加锁,或引擎层绕过行锁机制直接上表级锁。不同版本间逻辑一致,但诊断手段和默认行为有关键差异。
为什么EXPLAIN显示type: ALL就等于事实上的表锁
当 WHERE 条件没走索引,InnoDB 会全表扫描,并对每一行加 X 锁(或临键锁)。这不是“升级”,而是逐行加锁的自然结果,效果等同于锁表。
-
EXPLAIN中type为ALL或key为NULL,基本可判定未命中索引 - 常见诱因:
WHERE name LIKE '%admin%'(无索引)、WHERE city = 'SZ'(联合索引顺序错)、WHERE phone = 138(隐式类型转换) - 即使只更新 1 行,只要扫描了 10 万行,就会持有 10 万个行锁 → 阻塞严重、死锁风险高、事务回滚开销大
LOCK TABLES 和 ALTER TABLE 触发的是真表锁,不是“升级”
这类操作与行锁机制完全解耦,是 Server 层通过元数据锁(MDL)或显式指令直接施加的表级排他控制。
-
LOCK TABLES t WRITE:会话级独占锁,后续所有 DML 被拦,SELECT ... FOR UPDATE直接报ERROR 1100 -
ALTER TABLE/TRUNCATE TABLE:由 MDL 控制,阻塞所有并发 DML,效果等同表锁 - MyISAM 表任意写操作:该引擎不支持行锁,
UPDATE就是天然表锁 - MySQL 8.0+ 必须查
performance_schema.data_locks才能确认锁类型;SHOW ENGINE INNODB STATUS\G只显示最近事务,漏掉静默持有者
RR 隔离级别下临键锁扩大范围,容易误判为“锁升级”
同一句 UPDATE 在 RC 和 RR 下锁行为完全不同。这不是版本差异,而是隔离级别语义决定的。
- RC 级别:
UPDATE t SET a=1 WHERE b > 100只锁实际匹配的行,不加间隙锁,执行完即释放不匹配行的锁 - RR 级别:同样语句会加临键锁(Next-Key Lock),锁住
b > 100对应的整个索引区间,可能阻塞新插入 - RR 是 MySQL 默认隔离级别,也是互联网业务踩坑重灾区——你以为只改几行,其实锁了一大片
- 若业务能接受不可重复读,把隔离级别设为
READ-COMMITTED,能显著减少锁冲突
AUTO_INCREMENT 插入路径在高并发下接近表锁
这和 WHERE 条件无关,而是 InnoDB 在自增列分配时的保守策略。它不区分版本,但在高并发 INSERT 场景下表现突出。
- 未走主键/唯一索引查找路径时,InnoDB 会对整个索引段加锁,效果接近表锁
- 批量插入时若使用
INSERT ... SELECT或无明确主键值,也可能触发更宽泛的锁范围 - 避免在热点表上频繁执行无主键约束的
INSERT,尤其不要在事务中混合INSERT和UPDATE操作
真正难排查的从来不是“有没有锁”,而是“为什么这条明明带索引的语句,还是锁了整张表”。核心永远落在执行计划是否真实走索引、隔离级别是否被忽略、以及 MDL 是否被 DDL 意外持有——这些细节比“锁升级”这个说法重要得多。











