explain显示用了索引但查询仍慢,核心在于rows远高于实际返回行数、回表频繁或排序未覆盖;需检查选择性、最左前缀、覆盖索引及统计信息是否准确。

EXPLAIN 显示用了索引,但查询还是慢——这不是索引没生效,而是索引“没用好”。核心问题往往不在有没有索引,而在扫描行数太多、回表太频繁、或数据本身太重。
为什么 EXPLAIN 说用了索引,rows 却高得离谱?
这是最常被忽略的信号:key 字段非 NULL 只说明选了索引,不等于效率高。rows 是优化器预估要扫描的行数,一旦它远高于实际返回行数(比如查 10 条却扫 50 万行),说明索引选择性差或条件过滤能力弱。
- 用
COUNT(DISTINCT column_name)/COUNT(*)算字段选择性,低于 0.1 的字段(如status、gender)单独建索引意义很小 - 联合索引
(a, b, c)中,如果WHERE a = ? AND c = ?缺少b,实际只能用到a部分,后续条件全靠逐行过滤 -
LIKE '%abc'或WHERE YEAR(create_time) = 2023这类写法会让索引“形同虚设”——type可能是range,但rows暴涨
为什么查得少,但响应时间长?回表成本被低估了
InnoDB 的辅助索引不存完整行数据,只存主键值。只要 SELECT * 或查了索引没覆盖的字段,就得回到聚簇索引捞数据——一次回表是一次随机 I/O,10 万次回表 ≈ 10 万次磁盘寻道。
- 用
EXTRA列看是否出现Using index:有,说明是覆盖索引,不回表;没出现,基本就在回表 - 把常用查询字段塞进联合索引末尾,例如经常查
WHERE user_id = ? ORDER BY created_at DESC,就建(user_id, created_at, name, email)—— 后两个字段让查询免回表 - 避免
SELECT *,只查真需要的字段,尤其别带大字段(TEXT、BLOB),它们会强制回表且拖慢传输
为什么加了索引,ORDER BY 还触发 Using filesort?
排序不走索引,本质是索引顺序和 ORDER BY 不一致。即使 WHERE 走了索引,只要排序字段不在索引最右连续位置,MySQL 就得额外排序。
- 索引
(a, b)支持ORDER BY a, b或ORDER BY a,但不支持ORDER BY b或ORDER BY a DESC, b ASC(方向混搭) - 复合索引中,
WHERE条件字段必须是前缀,ORDER BY字段必须紧接其后且方向统一,才能避免Using filesort - 如果
ORDER BY字段区分度低(如时间字段重复率高),即使走了索引,也可能因大量相同值导致排序开销不减
为什么线上跑得慢,本地 EXPLAIN 却很快?
执行计划依赖统计信息,而统计信息可能过期或不准。InnoDB 默认每修改 10% 行就采样更新一次统计,大表或批量导入后极易失真。
- 运行
ANALYZE TABLE table_name;强制刷新统计信息,再看EXPLAIN的rows是否合理 - 检查
innodb_stats_auto_recalc是否开启(默认 ON),关了的话需手动维护 - 对比
SHOW INDEX FROM table_name中的Cardinality值和实际唯一值数量,偏差大就说明统计失效
rows 压到千级以内”、“能不能避开回表”、“能不能吞掉 ORDER BY”。这些细节在 EXPLAIN 里都写着,只是容易被当成装饰性字段略过。











