ANALYZE TABLE 能解决问题是因为它会重新采样索引页,更新统计信息(如 ROWS、CARDINALITY),使优化器基于真实数据分布选择更优索引,从而将慢查询从44秒降至毫秒级。

MySQL 选错索引,根本原因往往不是 SQL 写得有问题,而是优化器“算错了”——它基于过时的统计信息,误判走 idx_1 比走 idx_city_id_type 更快,结果扫了 8000w 行才捞出 1 条数据,耗时 44s。
为什么 ANALYZE TABLE 能解决问题
MySQL 优化器依赖表和索引的统计信息(比如行数、索引基数、值分布直方图)估算执行成本。这些统计不会实时更新,尤其在大批量 INSERT/DELETE 后极易滞后。当 city_id = 565 实际只匹配几百行,但统计显示该值“很常见”,优化器就可能放弃更精准的 idx_city_id_type,转而选一个看起来“覆盖更广”的索引,甚至全表扫描。
ANALYZE TABLE 会重新采样索引页,更新 STATS_INITIALIZED、ROWS、CARDINALITY 等关键字段,让优化器看到真实分布。实操中,对 sample_table 执行后,慢查询立刻回落至毫秒级。
- 仅对 InnoDB 表有效;MyISAM 表需用
myisamchk -a - 执行期间会加读锁,大表建议在低峰期操作
- 默认采样约 10–20 个索引页,可通过
innodb_stats_persistent_sample_pages调整精度
ANALYZE TABLE 不生效的常见原因
不是所有“选错索引”都能靠 ANALYZE TABLE 拯救。如果执行后执行计划没变,优先排查以下三点:
- 表被设置了
innodb_stats_persistent = OFF,且innodb_stats_on_metadata = OFF,导致统计信息不持久、不自动更新 - 查询中用了函数(如
WHERE DATE(log_dt) = '2024-01-01'),直接绕过索引,ANALYZE无能为力 - 存在隐式类型转换,比如
city_id是INT,但传参是字符串'565',优化器被迫在列上加转换函数,索引失效
FORCE INDEX 是临时止血,不是根治方案
当线上告警压顶、来不及分析时,FORCE INDEX (idx_city_id_type) 可立即生效,但它是硬编码式干预:
- 绕过优化器决策,丧失自适应能力:一旦数据分布突变(比如某
city_id突然暴涨 10 倍),强制走索引反而更慢 - 耦合业务 SQL,后续改索引名或删索引会直接报错
- 无法解决根本问题——统计不准仍会影响其他查询,只是这个 SQL 被你“按住”了
真正稳定的解法,是把 ANALYZE TABLE 加入运维巡检脚本,配合监控 information_schema.STATISTICS 中的 CARDINALITY 波动,提前发现偏差。
统计信息不准这件事,本身没有错误日志、没有告警、不抛异常,它安静地拖垮性能,直到慢查询像雪崩一样涌进来。最危险的,是你以为索引还在工作,其实它早已被优化器悄悄弃用。


















