mysql没走索引时会锁全表,因为innodb行锁实际加在索引记录上;全表扫描需对每条聚簇索引记录加x锁,等效锁全表。可通过explain检查type、key、rows三列判断是否走索引。

MySQL没走索引时为什么等于锁全表
InnoDB 的行锁不是加在“数据行”上,而是加在**索引记录**上。如果 WHERE 条件无法命中任何索引,优化器只能走聚簇索引(主键)做全表扫描,于是每扫描一条记录,就对那条聚簇索引记录加一个 X 锁——结果就是所有主键记录都被锁住。在大表上,这和锁全表没区别。
常见错误现象包括:SHOW ENGINE INNODB STATUS 显示成百上千个 lock_mode X locks rec but not gap;并发 UPDATE 响应时间突增;SELECT ... FOR UPDATE 长时间卡住。
怎么一眼看出没走索引
别猜,直接用 EXPLAIN 看执行计划,重点盯三列:
-
type:必须是const、ref、range之一,绝不能是ALL -
key:必须显示具体索引名,不能是NULL -
rows:预估扫描行数,应远小于表总行数(比如 100 行 vs 100 万行)
注意:EXPLAIN UPDATE ... 在 MySQL 8.0.19+ 可直接用,比先写 EXPLAIN SELECT 更准——因为 UPDATE 的执行路径和 SELECT 并不总一致。
哪些写法会让索引悄悄失效
不是建了索引就万事大吉,这些操作会绕过索引:
- 隐式类型转换:
user_id是INT,但写成WHERE user_id = '123'→ 字符串转数字后索引失效 - 函数包裹字段:
WHERE DATE(create_time) = '2024-01-01'→ 改用create_time BETWEEN '2024-01-01 00:00:00' AND '2024-01-01 23:59:59' - 最左前缀不满足:
INDEX(a, b, c),但查询只用WHERE b = ?→ 完全不走索引 - 使用
!=、NOT IN、LIKE '%abc'等非SARGable条件
分批更新也得小心锁范围
UPDATE ... LIMIT N 不减少扫描量,WHERE 还是全表扫,锁照样加满。真正安全的分批方式是:
- 用有索引且单调的字段做游标,比如主键
id或自增时间戳 - 每次限定范围:
UPDATE t SET status=2 WHERE id > 10000 AND id - 每批单独事务,执行完立刻
COMMIT,锁立即释放 - 批大小控制在 1000–5000 行:太小事务开销大;太大单次锁持有太久
最容易被忽略的是:建完索引后不验证,或者统计信息陈旧导致 EXPLAIN 结果不准——务必在建索引后跑一次 ANALYZE TABLE table_name 再测。











