小表查询选全表扫描不是bug而是优化器基于成本模型的理性决策;因小表数据常驻buffer pool,顺序扫描比索引+回表的多次随机I/O更高效,EXPLAIN中type=ALL但rows接近总行数属正常,强行加索引或FORCE INDEX反而增加开销。

小表查询时 MySQL 选全表扫描,不是 bug,是优化器基于成本模型的理性决策——哪怕你建了索引,它也可能直接跳过。
为什么小表(比如几百行)反而不走索引?
MySQL 优化器会估算两种路径的成本:走索引 + 回表 vs. 直接全表扫描。对小表来说,全表扫描可能更便宜:
- 全表扫描只需一次顺序 I/O(尤其数据在 buffer pool 里已缓存),而索引查找要先读索引页、再定位聚簇索引页,至少两次随机 I/O
- 如果查询
SELECT *或字段未被索引覆盖,走二级索引还得回表,额外开销翻倍 - 索引本身也有维护成本:B+ 树深度小、页数少,但遍历索引树仍比扫内存中几页数据慢
EXPLAIN 看到 type=ALL 就代表性能差?
不一定。关键看 rows 和实际执行时间:
-
rows=120(表共 150 行)+type=ALL→ 正常,扫得快,别硬加索引 -
rows=120但key=NULL+Extra里有Using where→ 确实没用索引,但影响微乎其微 - 真正该警惕的是
rows接近总行数却出现在大表上,比如百万行表rows=982341
强行加 FORCE INDEX 会让小表变快吗?
大概率不会,还可能更慢:
-
FORCE INDEX只告诉优化器“用这个索引”,不改变 WHERE 条件是否能命中索引 - 如果条件本身触发隐式转换(如
WHERE id = '123'而id是INT),索引根本不可用,FORCE无效 - 小表场景下强制走索引,反而引入不必要的 B+ 树导航和回表逻辑,CPU 和 I/O 开销双升
真正需要干预的,从来不是“小表要不要走索引”,而是确认查询是否真的需要索引——比如这张表未来会涨到 10 万行,现在写的 SQL 是否已在埋雷。索引设计要面向增长,而不是当前的 200 行。


















