小表建索引需看执行计划而非经验;优化器会基于成本选择全表扫描或索引,重点观察EXPLAIN中的type和rows值,结合buffer pool命中率、统计信息是否更新及查询实际需求(如避免filesort、支撑JOIN)综合判断。

小表要不要建索引?看执行计划比猜更靠谱
小表(比如几百行)加索引不一定快,有时反而拖慢查询。MySQL 的优化器在评估 WHERE 条件时,会权衡「走索引 + 回表」和「直接全表扫描」的成本。当数据量小、缓存命中率高、或查询条件选择性差时,优化器大概率放弃索引——这不是 bug,是成本模型的合理判断。
- 用
EXPLAIN SELECT ...看type字段:如果是ALL,说明走了全表扫描;ref/range才算用了索引 - 别只看「有没有索引」,重点看
rows列:它反映优化器预估扫描行数,比实际行数还小?那索引很可能被跳过了 -
SELECT *+ 小表 + 无 WHERE 条件 → 几乎必然全表扫描,加索引纯属冗余
哪些字段值得给小表加索引?不是所有 WHERE 都需要
小表索引的价值不在“加速”,而在「避免临时排序/分组」或「支撑连接顺序」。比如 JOIN 中作为被驱动表,或 ORDER BY 字段没覆盖索引时触发 filesort。
- 高频等值查询字段(如
status、type_id),且该字段在WHERE中出现频繁 → 值得建单列索引 -
ORDER BY created_at LIMIT 10这类查询,即使表只有 200 行,没索引也会触发Using filesort - 复合索引要匹配最左前缀:
(a, b)能加速WHERE a = ?或WHERE a = ? AND b = ?,但对WHERE b = ?无效 - 注意隐式类型转换:比如
user_id是VARCHAR,但查询写成WHERE user_id = 123(数字),索引会失效
全表扫描真的慢吗?要看数据是否在 buffer pool 里
小表全表扫描的物理 I/O 往往为 0,因为整个表可能早就被加载进 innodb_buffer_pool。这时扫描速度取决于内存带宽和 CPU,通常比走索引+回表更快——尤其当索引本身也得从磁盘读、且要多次随机 IO 时。
- 查
SHOW ENGINE INNODB STATUS\G里的Buffer pool hit rate,接近 99%+ 说明热数据基本都在内存 - 用
SELECT COUNT(*) FROM table_name触发一次扫描,再立刻EXPLAIN同一语句,rows值不变但执行时间明显下降 → 证明是内存扫描 - 如果表有 500 行但每行 10KB(含大文本字段),全表扫描传输量远大于走索引查主键再回表 → 这时索引反而更省带宽
建索引前先确认是不是统计信息过期了
MySQL 依赖表的统计信息(如索引基数、行数估算)做执行计划决策。小表如果长期没更新,ANALYZE TABLE 没跑过,优化器可能误判索引价值,导致本该走索引却选了全表扫描。
- 执行
ANALYZE TABLE your_table_name强制刷新统计信息,再看EXPLAIN结果是否变化 - 检查
information_schema.STATISTICS表中CARDINALITY字段:对唯一值少的字段(如is_deleted TINYINT),基数低 → 优化器认为索引区分度差,倾向不走 - 不要手动调
optimizer_switch强制走索引,小表场景下这容易掩盖真实瓶颈,比如锁竞争或网络延迟
真正卡住性能的,往往不是“该不该建索引”,而是没看清执行计划里那个被忽略的 Using temporary,或者没意识到 JOIN 顺序让小表成了驱动表却没索引可走。


















