ANALYZE TABLE 立即更新统计信息,但执行计划未变通常因优化器缓存旧统计、INFORMATION_SCHEMA访问绕过更新、或连接池复用旧计划;需FLUSH OPTIMIZER_COSTS(8.0.22+)、验证stats_modified_counter归零及EXPLAIN rows变化。

analyze table 执行后没生效?检查是否被自动禁用
MySQL 8.0+ 默认开启 innodb_stats_auto_rehash,但真正决定统计信息是否自动更新的,是 innodb_stats_on_metadata 和表访问方式。执行 ANALYZE TABLE 后发现执行计划没变,大概率是因为优化器缓存了旧统计,或你查的是 INFORMATION_SCHEMA 表(触发元数据扫描,可能绕过手动更新)。
-
ANALYZE TABLE是即时生效的,但优化器可能仍用旧的执行计划缓存 —— 需要FLUSH OPTIMIZER_COSTS(MySQL 8.0.22+)或重启连接 - 如果表启用了
innodb_stats_persistent=OFF,统计信息只存在内存里,重启 mysqld 就丢,必须设为ON并配innodb_stats_persistent_sample_pages - 分区表要注意:
ANALYZE TABLE默认只分析一级分区元数据,加FOR FULLSCAN(MySQL 8.0.23+)才能真正采样全量数据
定期执行 analyze table 的安全边界在哪
不是所有表都适合高频 ANALYZE TABLE,尤其大表会锁表(InnoDB 行级锁但会持有一个轻量级 MDL 写锁),阻塞 DDL;MyISAM 更严重,直接表级读锁。线上跑着 OLTP 流量时,一次 ANALYZE TABLE t1 可能导致慢查询堆积。
- 优先对「数据变更频繁 + 被高频 JOIN 或 WHERE 过滤」的表做,比如订单状态表、用户标签表;静态配置表没必要每周跑
- 避开业务高峰:用
pt-osc类工具做不到无锁分析,但可用SELECT COUNT(*)配合INFORMATION_SCHEMA.TABLES先判断行数变化率 >15% 再触发ANALYZE - MySQL 5.7 不支持并发
ANALYZE,8.0+ 可通过innodb_stats_include_delete_marked=ON减少误判,但无法并行执行多个ANALYZE
替代方案:用采样控制开销,而不是全表扫
默认 ANALYZE TABLE 用 20 个叶子页采样(innodb_stats_sample_pages=20),对 TB 级表远远不够,但盲目调高又拖慢执行。更稳的做法是按需指定采样深度,而非依赖默认值。
- 小表(ANALYZE TABLE t1
- 中等表(100 万–5000 万):设
innodb_stats_sample_pages=100,再执行ANALYZE TABLE t1 - 超大表(>1 亿):改用
ANALYZE TABLE t1 PERSISTENT FOR ALL COLUMNS(MySQL 8.0.23+),让持久化统计走异步后台线程,不卡前台 - 避免在从库执行 —— 主从延迟可能让从库统计比主库还新,导致路由到从库的查询走错索引
监控是否真在起作用:看 optimizer_trace 和 stats_modified_counter
光看 ANALYZE TABLE 返回 “OK” 没用,得验证优化器实际用了新统计。最直接的方式是查 INFORMATION_SCHEMA.INNODB_TABLESTATS 和跟踪执行计划变化。
- 执行后立刻查:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESTATS WHERE table_name = 't1',重点看stats_modified_counter是否归零(表示已刷新) - 对关键 SQL 开
SET optimizer_trace="enabled=on",执行后查information_schema.OPTIMIZER_TRACE,确认rows_estimation数值有明显变化 - 不要依赖
SHOW INDEX FROM t1—— 它只显示索引结构,不反映统计质量;EXPLAIN FORMAT=JSON里的rows字段才是统计生效的证据
真实环境里最容易被忽略的是:统计信息更新后,应用连接池里缓存的执行计划不会自动失效。哪怕你刚跑完 ANALYZE TABLE,老连接还在用旧的 rows 估算值,得靠连接重建或显式清空 query cache(如果开着)来推动切换。


















