MySQL优化器在小表(通常≤3–5页)上放弃索引选择全表扫描,是因随机I/O代价高于顺序I/O;强制索引反而降低QPS、增加延迟,且小表可能根本无需该索引。

MySQL在数据量较小时放弃索引,不是bug,也不是配置错误,而是优化器基于真实IO代价算出来的最优解——它发现全表扫描比走索引更快。
为什么小表全表扫描反而比索引快?
核心在于随机I/O vs 顺序I/O的代价差异。InnoDB中,哪怕只有10行数据,也至少占1个16KB数据页;而走二级索引要先读索引页(随机跳转),再回表查聚簇索引(又一次随机跳转),共2次随机I/O。全表扫描只需顺序读1次数据页,且若该页已在buffer pool中,几乎全是内存操作。
-
innodb_buffer_pool_size足够大时,小表整页常驻内存,全表扫描≈内存遍历 - 索引树本身有层级开销:即使B+树只有1层,也要多一次页定位、校验、指针跳转
- SELECT *强制回表时,每行都触发一次额外随机访问,小表里这个开销占比反而更高
怎么判断一张表“够小”?
没有绝对行数阈值,关键看物理页数是否 ≤ 3–5页(即数据长度 ≈ 50KB–80KB)。用这条SQL估算:
SELECT DATA_LENGTH / 16384 AS data_pages FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';
- data_pages ≤ 3 → 优化器大概率跳过索引,type=ALL但rows极小(如≤10)是正常现象
- EXPLAIN中
Extra为空或仅Using where,且无Using index condition,说明没走索引但也没做多余操作 - 别被
key=NULL吓到——它只表示这次没选索引,不等于索引无效或损坏
FORCE INDEX会起反作用吗?
会,而且很典型。加FORCE INDEX只是绕过成本计算,强制走索引路径,但无法改变底层I/O事实:
- 小表上强制走索引,实际执行会多读1次索引页 + 多次回表跳转,QPS下降、延迟升高
- EXPLAIN仍显示type=ref或range,但真实耗时可能翻倍——因为优化器原本就对了
- 这种“修复”掩盖了真正问题:你本不该为这张表建这个索引
最容易被忽略的是:小表是否真需要这个索引?如果WHERE条件过滤后仍返回大量行,或查询频率极低,索引维护成本(写放大、内存占用、统计更新)可能长期高于它带来的收益。


















