必须手动执行ANALYZE TABLE的场景包括:大批量导入数据后、大范围UPDATE/DELETE导致列分布剧变、新建索引后首次查询慢、慢查询执行计划异常且EXPLAIN显示rows估算偏差超一个数量级。

ANALYZE TABLE 什么时候必须手动执行
MySQL 的 ANALYZE TABLE 不是“一劳永逸”的操作,它只在统计信息过期或严重失准时才值得触发。InnoDB 默认会自动采样更新统计信息(innodb_stats_auto_recalc=ON),但这个机制有明确限制:只有当表中约 10% 的数据发生变更时,才会触发自动更新。这意味着小表可能长期不更新,大表即使改了百万行,只要没跨过 10% 阈值,优化器看到的仍是旧分布。
常见必须手动跑的场景包括:
- 刚导入大量历史数据(比如从 CSV 批量
LOAD DATA INFILE后) - 执行完大范围
UPDATE/DELETE(尤其影响选择性高的列,如status列从 95% 'active' 变成 40% 'active') - 新建索引后首次查询性能差——优化器还没见过这个索引的基数(cardinality)
- 慢查询执行计划突然走错(比如该走
idx_user_id却用了全表扫描),且EXPLAIN显示rows估算偏差超一个数量级
ANALYZE TABLE 和 FORCE 的区别与风险
ANALYZE TABLE t1 是默认行为,InnoDB 用随机页采样估算统计信息,快但不够精确;加 FORCED(即 ANALYZE TABLE t1 FORCED)会让 InnoDB 扫描全部索引页,生成更准的统计,代价是锁表时间显著变长、I/O 压力大,且在高并发写入时可能阻塞 DML。
别盲目加 FORCED,除非你确认:
- 表小于 1GB,且业务低峰期可接受秒级锁表
- 普通
ANALYZE后SHOW INDEX FROM t1中关键索引的Cardinality值明显不合理(比如唯一索引显示Cardinality = 1) - 对比过
information_schema.STATISTICS里SEQ_IN_INDEX和实际数据分布,发现采样严重偏斜
注意:FORCED 在 MySQL 8.0.23+ 已被弃用,推荐改用 ANALYZE TABLE t1 PERSISTENT FOR ALL(需先开启 innodb_stats_persistent)。
统计信息不准导致的典型执行计划误判
最常被忽略的是“索引选择性误估”:比如 created_at 上有索引,但统计信息认为该列高度重复(Cardinality 很低),优化器就放弃走索引,改用全表扫描——哪怕你只查最近 1 小时的数据。这种问题不会报错,只会默默变慢。
验证是否是统计问题,分两步:
- 查当前统计:
SELECT TABLE_NAME, INDEX_NAME, CARDINALITY FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='your_db' AND TABLE_NAME='t1' ORDER BY SEQ_IN_INDEX; - 人工估算:对关键列运行
SELECT COUNT(DISTINCT created_at) / COUNT(*) FROM t1;,如果结果接近 1(高选择性),但CARDINALITY却远小于总行数,基本就是统计滞后
另一个坑是分区表:ANALYZE TABLE 默认只分析元数据,不分析每个分区,得显式写成 ANALYZE TABLE t1 PARTITION(p202401)。
生产环境执行 ANALYZE 的安全习惯
别在高峰期直接敲命令。InnoDB 虽支持在线 ANALYZE(不阻塞读,但会短暂阻塞写),但锁竞争和 I/O 尖刺仍可能波及其他查询。
- 优先用低权重方式:
SET SESSION innodb_stats_sample_pages = 25; ANALYZE TABLE t1;(默认是 20,提高到 25 能小幅提升精度,又不显著拖慢) - 避开主从延迟敏感时段:因为
ANALYZE是 DDL,会写 binlog,从库重放期间也可能卡住复制线程 - 避免在
autocommit=0事务里执行——它会隐式提交当前事务,容易引发意外交互 - 监控后续效果:执行后立刻跑
EXPLAIN FORMAT=JSON SELECT ...,重点看rows_estimated是否贴近真实返回行数
真正麻烦的不是怎么跑 ANALYZE,而是你怎么知道它该跑了——得盯住 slow_log 里反复出现的“本该走索引却没走”的查询,再反查统计信息,这一步最容易被跳过。










