ANALYZE TABLE能快速修复MySQL优化器选错索引问题,因其重采样索引页更新统计信息;失效主因是统计未持久化、函数/隐式转换导致索引失效,FORCE INDEX仅临时止血,长效方案是纳入巡检并监控CARDINALITY偏差。

MySQL优化器选错索引,绝大多数时候不是SQL写错了,也不是索引建得不对,而是它“算错了”——基于过时的统计信息误判了成本。直接执行 ANALYZE TABLE 通常就能让慢查询从秒级回落到毫秒级。
为什么ANALYZE TABLE能立刻见效
优化器依赖 CARDINALITY(基数)、ROWS(预估行数)等统计值估算扫描成本。这些值不是实时更新的,而是通过采样索引页计算得出。当表经历大批量 INSERT/DELETE 后,采样结果严重偏离真实分布,比如:
-
city_id = 565实际只匹配 200 行,但统计显示该值“高频”,优化器就放弃idx_city_id_type,转而扫 8000w 行 - 复合索引
(a, b)中a列区分度突降,但统计仍按旧值估算,导致优化器误判为低选择性
ANALYZE TABLE 会重新随机采样约 10–20 个索引页(可调),刷新 information_schema.STATISTICS 中的关键字段,让优化器看到真实数据分布。
ANALYZE TABLE不生效的三个典型原因
执行完 ANALYZE TABLE 后 EXPLAIN 没变?先排查这三点:
- 表开启了
innodb_stats_persistent = OFF,且innodb_stats_on_metadata = OFF,导致统计信息只存在内存里,重启或元数据操作后立即失效 - 查询中用了函数,比如
WHERE DATE(log_dt) = '2024-01-01',索引根本用不上,ANALYZE对失效索引无能为力 - 存在隐式类型转换,例如
city_id是INT,但传参是字符串'565',优化器被迫在列上加转换函数,索引失效
FORCE INDEX 是止血,不是根治
线上告警压顶时,SELECT * FROM t FORCE INDEX (idx_city_id_type) WHERE city_id = 565 能立刻生效,但它掩盖了真正的问题:
- 绕过优化器决策,一旦某
city_id数据量暴涨十倍,强制走索引反而更慢 - SQL 硬编码索引名,后续删索引或改名会直接报错
ERROR 1176 (HY000): Key 'xxx' doesn't exist in table 't' - 其他没加
FORCE的查询依然受错误统计影响,问题只是被局部压制
统计不准这件事本身没有日志、不报错、不告警
它安静地拖垮性能,直到慢查询像雪崩一样涌进来。最危险的是你以为索引还在工作,其实优化器早已悄悄弃用。真正稳定的解法,是把 ANALYZE TABLE 加入运维巡检脚本,并监控 information_schema.STATISTICS.CARDINALITY 的波动趋势——偏差超过 30%,就自动触发重采样。


















