MySQL优化器选错索引表现为EXPLAIN显示key为空或低效索引、本该走范围扫描却全表扫描且Rows_examined极高;根本原因是统计信息过时或失真,FORCE INDEX可强制指定索引但需谨慎验证并配合ANALYZE TABLE等长效优化。

MySQL优化器选错索引的典型现象
你执行 EXPLAIN 后发现 key 字段为空或指向一个明显低效的索引,而实际查询慢得反常;更典型的是:明明 WHERE a BETWEEN 10000 AND 20000,却走了全表扫描,Rows_examined 高达十万级——这不是语句写错了,是优化器基于过时或失真的统计信息做了错误成本估算。
FORCE INDEX 是最直接的干预手段
它绕过优化器评估,强制走指定索引,适用于已确认该索引在真实数据分布下更优的场景:
SELECT * FROM order_log FORCE INDEX (idx_create_time) WHERE create_time >= '2024-01-01';- 注意:必须用括号包裹索引名,且不能加表别名(
FORCE INDEX (t.idx_create_time)会报错) - 若索引名含特殊字符或大小写混合,需用反引号包裹:
FORCE INDEX (`idx_create_time_status`) - 不支持同时指定多个索引,也不能用于
UPDATE或DELETE语句(MySQL 8.0+ 仍不支持)
强制前必须验证的三件事
跳过验证直接上线 FORCE INDEX 是高风险操作:
- 对比
EXPLAIN输出:原SQL 和加提示后的key、rows、Extra(尤其看是否出现Using filesort或Using temporary) - 在准生产环境用全量时间范围压测:观察
Query_time、Rows_examined、CPU 和 I/O 消耗,小数据量快 ≠ 大数据量稳 - 设置熔断:应用层加 SQL 超时(如 MyBatis 的
timeout),DBA 侧配置慢日志告警(例如执行时间 > 原基准 150% 即触发)
比强制更可持续的解法
强制索引是临时止痛药,长期依赖会掩盖根本问题:
- 定期运行
ANALYZE TABLE order_log;—— 尤其在大批量INSERT/DELETE后,不要等“下次自动采样” - 避免函数导致索引失效:把
WHERE DATE(create_time) = '2024-01-01'改成WHERE create_time >= '2024-01-01' AND create_time - 按高频报表条件建覆盖索引:
CREATE INDEX idx_rpt ON order_log (create_time, status) INCLUDE (order_id, amount);(MySQL 8.0+ 支持INCLUDE,否则用联合索引)
真正容易被忽略的是:统计信息更新后,优化器可能仍沿用旧计划缓存。哪怕 ANALYZE TABLE 成功,也要确认查询是否触发了计划重编译——有时需要 FLUSH TABLES 或重启连接才能生效。


















