innodb未走索引时并非主动升级锁粒度,而是被迫全表扫描并逐行加record锁(含间隙锁),效果等同表锁;explain显示type: all即为明确信号,隐式转换、联合索引误用、like前缀模糊匹配等均会导致索引失效。

WHERE条件没走索引,InnoDB只能全表扫描
批量更新卡住、其他事务被阻塞,不是因为InnoDB“主动升级”锁粒度,而是优化器发现WHERE条件无法使用索引,被迫执行全表扫描。每扫描一行,InnoDB就加一个RECORD锁(含间隙锁),最终锁住成千上万行——效果等同于表锁,但锁类型仍是行锁。
-
EXPLAIN显示type: ALL就是明确信号,比如UPDATE user SET status = 1 WHERE name LIKE '%admin%'(name无索引) - 联合索引失效也算:建了
(city, name),却只用WHERE name = 'xxx',照样全扫 - 隐式类型转换会让索引失效:
WHERE phone = 138(phone是VARCHAR)→ 实际执行WHERE CAST(phone AS SIGNED) = 138,跳过索引
批量操作本身不触发升级,但放大无索引的后果
单条UPDATE没索引也会锁全表,只是影响小;批量操作(如UPDATE ... WHERE create_time )让问题暴露得更剧烈:锁住几万行、事务长时间不提交、后续DML全部排队等待。
- 别信“5000行就升级”的说法——InnoDB没有内置阈值开关,只有“能走索引就精准加锁,不能就逐行锁”这一条逻辑
- 分批执行(如
LIMIT 1000)不是防止升级,而是减少单次事务锁行数,降低阻塞时长和内存压力 -
DELETE、SELECT ... FOR UPDATE同理:没索引 → 全扫 → 锁所有扫描行
怎么确认是不是真锁了整张表?
别看SHOW ENGINE INNODB STATUS\G里模糊的“LOCK WAIT”,它只报最近事务,漏掉静默持有者。MySQL 8.0+ 必须查performance_schema.data_locks:
SELECT LOCK_TYPE, LOCK_MODE, INDEX_NAME FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
-
LOCK_TYPE = 'TABLE'→ 真表锁(比如LOCK TABLES或DDL) -
LOCK_TYPE = 'RECORD'+INDEX_NAME = 'PRIMARY'且行数远超预期 → 就是“伪表锁”:全表扫描导致的海量行锁 - 配合
performance_schema.data_lock_waits查谁在等谁,比INNODB_LOCK_WAITS更准
加索引就能解决?小心这些陷阱
补索引是最直接解法,但不是贴上就灵。常见踩坑点:
- 单列索引对
WHERE a = 1 AND b = 2效果有限,优先建联合索引(a, b),顺序按字段选择性从高到低排 -
LIKE '%abc'这种前导通配符,加任何B-tree索引都没用,得换FULLTEXT或外部搜索服务 - 写多读少的表,高频
INSERT/UPDATE下,过多索引会拖慢写性能,甚至引发索引树latch竞争 - 字符集不一致也会让索引失效:比如
utf8mb4列 vsutf8字符串比较,隐式转换导致全扫











