InnoDB行锁不会升级为表锁,而是因索引失效导致全表扫描,进而对每行聚簇索引记录加行锁并配合间隙锁,效果等同锁表。

索引失效时,InnoDB根本没走行锁路径
MySQL 的行锁不是“升级”成表锁,而是压根没机会用行锁——因为 InnoDB 的行锁必须依附于索引结构才能定位具体记录。一旦 WHERE 条件无法命中索引(比如字段无索引、隐式类型转换、函数包装),优化器就会选择全表扫描,此时加锁行为就变成:对**每一条扫描到的聚簇索引记录**都加 Record Lock,再配合 Gap Lock 封住所有间隙。效果上锁了整张表,但底层仍是行锁集合,不是真正的表锁。
哪些写法会让索引瞬间失效?
常见但极易被忽略的索引失效场景:
-
WHERE name = 123(name是VARCHAR,传入数字触发隐式转换) -
WHERE DATE(create_time) = '2025-06-01'(函数导致索引无法下推) -
WHERE name LIKE '%abc'(前导通配符,B+树无法利用) -
WHERE status = '1'(status是TINYINT,字符串比较强制转换) - 联合索引
(a,b,c),但查询只用了WHERE b = 1 AND c = 2(违反最左前缀)
怎么确认是不是索引失效导致锁范围失控?
别猜,直接看执行计划和锁状态:
- 跑
EXPLAIN FORMAT=JSON,重点检查:"key": null或"access_type": "ALL" - 查
information_schema.INNODB_TRX,如果TRX_ROWS_LOCKED值远超业务预期(比如改 1 行却锁了 5 万行),基本就是全表扫描加锁 - 在另一个会话执行
SELECT * FROM t WHERE pk = ? FOR UPDATE被阻塞,说明前一个事务已覆盖全表加锁 - 开启
log_queries_not_using_indexes = ON,配合慢日志抓出所有“没走索引”的 DML
加索引就能万事大吉?
补索引是解法,但不是无脑操作:
- 单列索引对
WHERE a=1 AND b=2效果有限,优先建联合索引(a,b),顺序按选择性从高到低排 - 高频写表加太多索引,反而拖慢
INSERT/UPDATE,并加剧索引树 latch 竞争 -
LIKE '%abc'这种没法靠 B+ 树索引解决,得换FULLTEXT或倒排结构 - 字符集不一致(如
utf8mb4列 vsutf8参数)也会让索引失效,查SHOW CREATE TABLE和客户端连接字符集
真正卡点不在“要不要加索引”,而在于是否理解每条 SQL 实际走的是哪条索引路径、扫描了多少行、锁住了哪些记录——这些信息都在 EXPLAIN 和 INNODB_TRX 里,不看就等于蒙眼调优。


















