analyze table 更新统计信息但不立即生效,需检查优化器缓存、innodb_stats_persistent状态及txsql_recalc_table_stats_after_manual_close等参数是否开启。

ANALYZE TABLE 能改善执行计划,但不是一执行就立刻生效——它只更新统计信息,优化器是否用新数据,取决于缓存、参数配置和查询上下文。
执行 ANALYZE TABLE 后执行计划没变?先查这三件事
很多人执行完 ANALYZE TABLE t1 就去 EXPLAIN,发现执行计划纹丝不动,以为命令失效。其实问题往往不在命令本身:
- 优化器缓存了旧的代价估算,尤其 MySQL 8.0.22+ 之前不支持
FLUSH OPTIMIZER_COSTS,得断开连接或重启会话才能刷新 - 你查的是
INFORMATION_SCHEMA.TABLES或执行SHOW TABLE STATUS,这类元数据访问可能触发innodb_stats_on_metadata=ON的自动采样,绕过你刚手动更新的结果 -
stats_modified_counter没归零(查mysql.innodb_table_stats),说明 InnoDB 实际没写入新统计,常见于innodb_stats_persistent=OFF且 mysqld 刚重启过
innodb_stats_notify_change 和 txsql_recalc_table_stats_after_manual_close 必须开
TDSQL for MySQL 或部分定制版 MySQL 中,这两个参数关着,ANALYZE TABLE 就是“做了白做”:
-
innodb_stats_notify_change=OFF:InnoDB 即使更新了索引基数(如rec_per_key),也不会主动通知 Server 层优化器,优化器继续用内存里缓存的老值 -
txsql_recalc_table_stats_after_manual_close=OFF:表被关闭再重新打开(比如 DDL 后),也不会触发统计重算,进一步锁死陈旧状态 - 修改必须走
mysql_param_modify工具,直接SET GLOBAL无效;改完要验证SHOW VARIABLES确认值为ON
大表 ANALYZE TABLE 别硬跑,默认采样太糙
默认只扫 20 个叶子页(innodb_stats_sample_pages=20),对千万级以上表,估算的 CARDINALITY 常严重失真,导致优化器误判索引选择性:
- 别盲目调高
innodb_stats_sample_pages—— 从 20 改到 200,执行时间可能从秒级变成分钟级,还未必更准 - MySQL 8.0.23+ 可用
ANALYZE TABLE t1 FOR FULLSCAN强制全量采样,但仅限低峰期、小流量窗口使用 - 更稳的做法:先用
SELECT COUNT(*)对比TABLE_ROWS,偏差 >15% 再触发ANALYZE,并加timeout 300防卡死 - 分区表注意:
ANALYZE TABLE t1默认只更新一级分区元数据,必须显式指定PARTITION(p1)或用FOR FULLSCAN
从库别碰 ANALYZE TABLE,主从统计会错位
从库执行 ANALYZE TABLE 不复制到主库,但会导致从库统计比主库“新”,而读请求若路由到从库,优化器可能基于更激进的基数选错索引:
- 主从延迟存在时,从库统计可能反映的是几分钟前的状态,而主库已因写入发生倾斜
- GTID 模式下,
ANALYZE TABLE在从库可能干扰 SQL Thread,尤其配合replica_parallel_workers > 0时 - 监控真生效与否,不能只看返回 “OK”,得查
mysql.innodb_table_stats.last_update时间戳 +EXPLAIN的rows是否明显变化
ANALYZE TABLE,而是没意识到统计信息从写入磁盘、通知优化器、到最终被某条查询采用,中间隔着至少三层缓存和两个开关。漏掉任意一环,就等于白跑。











