ANALYZE TABLE在MySQL 5.7中仅重采样索引页并更新mysql.innodb_table_stats和mysql.innodb_index_stats中的统计值(如cardinality、n_diff_pfx01),直接影响EXPLAIN的rows估算和索引选择,不刷新缓存、不重建索引、不修改数据。

ANALYZE TABLE在MySQL 5.7里到底更新了什么
它不刷新缓存、不重建索引、也不改变数据,只重采样索引页并更新mysql.innodb_table_stats和mysql.innodb_index_stats两张系统表里的统计值,比如cardinality(基数)、n_diff_pfx01等列。这些值直接影响EXPLAIN中rows的估算和优化器是否选择某个索引。
MySQL 5.7默认用innodb_stats_sample_pages = 8,即只读取每个索引的约8个随机叶子页——对千万级以上表,这个量级常导致cardinality严重低估,比如实际有百万唯一值,却报出几万。
执行前必须确认的三件事
- 确保表是InnoDB引擎:
SHOW CREATE TABLE t1里看到ENGINE=InnoDB;MyISAM会加表级读锁,5.7中应避免 - 检查是否启用了持久化统计:
SELECT @@innodb_stats_persistent返回ON才保证重启不失效;若为OFF,执行完ANALYZE TABLE后重启mysqld,统计就清零 - 确认没在GTID复制环境中误操作:MySQL 5.7.1要求执行前设
SET gtid_next = 'AUTOMATIC',否则报错ERROR 1785;5.7.2+已修复,但线上仍建议先查版本SELECT VERSION()
怎么让ANALYZE TABLE对大表真正有效
默认采样太粗糙,尤其当字段含大量重复值(如状态码、租户ID)或使用UUID前缀时,cardinality失真会直接导致优化器弃用本该高效的索引。
MySQL 5.7不支持WITH N SAMPLES语法,只能全局调参:
- 临时加大采样页数:
SET GLOBAL innodb_stats_sample_pages = 24(注意:仅影响后续新执行的ANALYZE TABLE,不回溯已有统计) - 执行分析:
ANALYZE TABLE t1 - 验证效果:查
SELECT * FROM mysql.innodb_index_stats WHERE table_name = 't1',对比n_diff_pfx01是否明显上升 - 执行完建议恢复原值,避免影响其他小表分析耗时
别设到100——SSD上24~64页通常是误差收敛与I/O开销的平衡点;机械盘建议不超过32。
常见“执行了但没生效”的真实原因
不是命令没跑,而是结果被绕过或覆盖:
-
EXPLAIN仍走错索引?可能连接复用了旧执行计划缓存;尝试断开重连,或换一个SELECT语句触发新解析 - 从库上执行了
ANALYZE TABLE?STATEMENT模式下该命令会被复制,主从统计信息可能冲突,导致执行计划不一致;应只在主库执行 - 分区表只跑了
ANALYZE TABLE t1?5.7默认不递归分析子分区;必须显式指定:ANALYZE TABLE t1 PARTITION (p2025_q1, p2025_q2) - 刚执行完
ALTER TABLE ADD INDEX就查SHOW INDEX?新索引的Cardinality列为0是正常的,ANALYZE TABLE后才会填充
最易被忽略的一点:ANALYZE TABLE本身不写binlog(除非显式关掉NO_WRITE_TO_BINLOG),但在复制环境里,它仍会传播到从库——这点在评估主从一致性时必须纳入考虑。


















