InnoDB行锁不会升级为表锁,所谓“锁表”是全表扫描时对所有扫描行加行锁及间隙锁的叠加效果;根本原因是未命中索引导致执行路径退化,EXPLAIN中type=ALL、key=NULL或rows远超预期即为危险信号。

行锁退化成表锁效果,根本不是“升级”
MySQL InnoDB 的行锁永远不会变成真正的表锁。所谓“锁表”,是大量行锁 + 间隙锁叠加后,实际阻塞了所有写入和部分插入,效果等同于表不可用。关键在于:InnoDB 加锁行为完全依赖执行路径——没索引,就只能全表扫描,扫到哪锁到哪。
EXPLAIN 看出问题的三个硬指标
别等线上卡死才查,执行前必须跑 EXPLAIN。重点关注:
-
type是ALL或index→ 没走有效索引,危险 -
key显示NULL→ 完全没用上索引 -
rows数值远超你预期匹配行数(比如 WHERE id = 123 却显示 rows=89241)→ 扫描失控
加了索引还锁全表?这些写法会让索引失效
索引存在 ≠ 被用上。常见失效场景:
-
WHERE phone LIKE '%138'→ 前导通配符,B+树无法下推 -
WHERE DATE(create_time) = '2026-01-01'→ 函数包裹字段,索引失效 -
WHERE b = 123,但联合索引是(a, b)→ 不满足最左前缀,跳过 -
WHERE user_id = 123,而user_id是VARCHAR→ 隐式类型转换,触发全表扫描
SELECT FOR UPDATE 和 UPDATE 一样危险
很多人以为 SELECT ... FOR UPDATE 总是安全的行锁,其实它和 UPDATE 共享同一套加锁逻辑。没索引时,它也会逐行加锁,甚至更隐蔽——因为不改数据,容易被忽略。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
例如:SELECT * FROM orders WHERE status = 'pending' FOR UPDATE,若 status 无索引,就会锁住所有主键记录,新订单插入直接被间隙锁堵死。
真正容易被忽略的是:锁范围不仅取决于 WHERE 条件匹配的行,还取决于索引结构、隔离级别、以及是否命中唯一索引——哪怕只查一条,没走唯一索引,也可能带上间隙锁。










