ANALYZE TABLE是刷新索引统计信息最直接有效的手段,用于防止优化器因cardinality等统计过时而选错索引、导致EXPLAIN中rows估算严重失真;必须手动执行的场景包括:批量导入/大范围DML后、新建索引首次查询慢、rows估算偏差超一个数量级、MySQL升级后统计失效。

索引统计信息过时会导致优化器选错索引、执行计划变差,ANALYZE TABLE 是最直接有效的刷新手段。它不改数据、不重建索引,只重采样并更新 cardinality、n_diff_pfx01 等关键值,直接影响 EXPLAIN 中的 rows 估算和索引选择。
什么情况下必须手动跑 ANALYZE TABLE
MySQL 的自动统计更新有明确阈值(默认变更超 10% 行数才触发),很多场景下它根本不会动——这时就得人工干预:
- 刚用 LOAD DATA INFILE 或 INSERT ... SELECT 批量导入大量数据后
- 执行了大范围 DELETE 或 UPDATE(比如把 status 列从 95% 'active' 改成 30% 'active')
- 新建索引后首次查询明显变慢,EXPLAIN 显示该索引 cardinality = 1 或远低于实际唯一值
- 某条原本快的查询突然变慢,且 EXPLAIN 中 rows 估算偏差超过一个数量级(如预估 100 行,实际扫描百万行)
- MySQL 升级后(尤其跨大版本),旧统计格式可能失效或被忽略
执行前要确认的三件事
避免报错或白忙一场,动手前快速核对:
- 确认表引擎是 InnoDB:用 SHOW CREATE TABLE tbl_name 查看 ENGINE=InnoDB;MyISAM 表会加表级读锁,5.7+ 不建议在线执行
- 检查是否启用了持久化统计:SELECT @@innodb_stats_persistent 返回 ON 才保证重启不失效;若为 OFF,ANALYZE 后重启 mysqld,统计就清零
- 确认不在 GTID 强制一致性环境里误操作:MySQL 5.7.1–5.7.2 期间执行前需先设 SET gtid_next = 'AUTOMATIC',否则报 ERROR 1785;建议先查版本 SELECT VERSION()
怎么让结果更准一点
默认采样页数(innodb_stats_sample_pages = 20)对千万级以上表常导致 cardinality 严重低估。可临时调高再执行:
- 查看当前设置:SELECT @@innodb_stats_sample_pages
- 临时调高(例如到 100):SET SESSION innodb_stats_sample_pages = 100
- 再执行:ANALYZE TABLE t1
- 注意:不要盲目加 FORCED(已弃用于 8.0.23+),除非表很小且业务低峰期允许秒级锁表
执行后没生效?别急着重跑
ANALYZE TABLE 本身是即时写入系统表的,但你看到的“没变化”往往卡在别处:
- 优化器可能缓存了旧计划:MySQL 8.0.22+ 可执行 FLUSH OPTIMIZER_COSTS 清空成本缓存
- 查的是 INFORMATION_SCHEMA.STATISTICS?它的统计值默认缓存 24 小时(information_schema_stats_expiry = 86400),想实时看就设为 0
- 连接池复用了旧连接:换新连接再 EXPLAIN,或验证 SELECT * FROM mysql.innodb_table_stats WHERE table_name = 't1' 中 stat_value 是否已更新
- 分区表默认只分析元数据:MySQL 8.0.23+ 需加 ANALYZE TABLE t1 FOR FULLSCAN 才真正全量采样


















