analyze table是mysql官方唯一支持的索引统计重采样标准操作,仅对innodb有效;需先确认统计失真(explain rows与实际行数差一个数量级、cardinality为0/1但列高区分度、count(distinct)与cardinality相差超10倍),并排除函数使用、隐式转换等干扰,再检查innodb_stats_persistent=on、引擎为innodb等配置,方可执行。

ANALYZE TABLE 是 MySQL 官方唯一支持的、能真正触发索引统计重采样的标准操作,对 InnoDB 表有效;它不保证 100% 修准,但所有“优化器选错索引”类慢查询,只要根源是 CARDINALITY 偏离真实分布,就该从它入手。
怎么确认真是统计信息不准,而不是 SQL 或索引本身有问题?
别一慢就跑 ANALYZE TABLE。先交叉验证三件事:
- EXPLAIN 中
rows预估和实际扫描行数差一个数量级以上(比如预估 420,SELECT COUNT(*)返回 38 万) -
SHOW INDEX FROM table_name里某列的CARDINALITY是 0 或 1,但你知道该列几乎每行都不同(例如主键、订单号、用户 ID) -
SELECT COUNT(DISTINCT col) FROM table_name和对应索引的CARDINALITY相差超 10 倍(比如前者 120 万,后者显示 86)
同时排除干扰:WHERE 条件里有没有用 DATE(created_at) 这类函数?传参类型是否和字段不一致(比如 user_id = '123' 对应 INT 字段)?这些会让索引直接失效,ANALYZE TABLE 对它们完全无效。
执行 ANALYZE TABLE 前必须检查的三个配置项
否则大概率白执行一次:
-
innodb_stats_persistent必须为ON(MySQL 8.0+ 默认开,5.7 升级后常残留OFF)——关了的话,统计只存在内存,重启即丢 -
innodb_stats_on_metadata建议设为OFF(默认ON)——否则SHOW TABLE STATUS会偷偷触发分析,拖慢元数据查询 - 确认表引擎是
InnoDB:SHOW CREATE TABLE table_name查ENGINE=InnoDB;MyISAM 表要用myisamchk -a,且 ANALYZE TABLE 效果极差
大表或数据倾斜严重时,怎么让 ANALYZE TABLE 更准?
默认只采样约 20 个索引页(由 innodb_stats_persistent_sample_pages 控制),对千万行以上或 95% 值为 1 的字段,极易崩成 CARDINALITY=1:
- 临时提精度:执行前运行
SET GLOBAL innodb_stats_persistent_sample_pages = 128,分析完再设回(注意该变量动态生效) - 极端倾斜列(如状态字段):用
ANALYZE TABLE t WITH SAMPLE 100 PERCENT强制全量扫描(仅限 MySQL 8.0.23+) - 分区表要指定分区:
ANALYZE TABLE t PARTITION(p2024),否则默认只刷元数据,不碰实际数据页
执行期间加表级读锁,会阻塞 DROP TABLE、ALTER TABLE 和并发另一个 ANALYZE TABLE,务必避开高峰期。
执行完发现 EXPLAIN 没变,怎么办?
不是命令失败,而是你没验证它是否真生效:
- 别信 “Query OK”,立刻查
information_schema.STATISTICS:SELECT column_name, cardinality FROM information_schema.STATISTICS WHERE table_name = 't' AND index_name = 'idx_col' - 如果
CARDINALITY没变,可能是被FORCE INDEX或USE INDEX硬编码覆盖,优化器根本没走统计决策路径 - 也可能是存在更优的复合索引(比如
(a, b)覆盖了单列a的查询),优化器选了它而非你期待的那个
统计不准这事从不报错,安静地拖垮性能。真正管用的做法,是把它变成低峰期自动巡检动作——比如每天凌晨对 DML 变更超 5% 的表触发 ANALYZE TABLE,并监控 CARDINALITY 波动是否超过 30%。











