ANALYZE TABLE 不能保证修好 Cardinality,但它是唯一标准手段;它触发采样更新统计,效果取决于锁状态、采样页数及持久化设置,适用于索引失效、估算严重偏差等场景。

ANALYZE TABLE 真的能修好 Cardinality 吗
不能保证修好,但它是唯一标准手段。Cardinality 是 MySQL 估算索引选择性的依据,不是精确统计值,而是采样估算结果。InnoDB 默认只采样少量数据页(innodb_stats_sample_pages,默认 20),样本少 + 表数据分布不均 + 大量重复值,就会导致 Cardinality 显著偏离真实唯一值数量。
执行 ANALYZE TABLE 会触发重新采样和统计更新,但效果取决于:表是否被锁(仅读锁,不影响写)、采样页数是否足够、是否启用了持久化统计(innodb_stats_persistent = ON)。
- 如果表刚大批量 INSERT/DELETE/UPDATE 过,必须执行,否则优化器可能选错索引
- 对超大表(比如 >100GB),默认采样可能仍不准,需临时调高
innodb_stats_sample_pages - MySQL 8.0+ 默认开启
innodb_stats_persistent,统计信息存到磁盘,重启不丢失;5.7 及以前默认关闭,重启后首次查询前才会自动分析
什么时候必须手动 ANALYZE TABLE
不是“定期跑一下”就完事,得看现象。以下情况手动执行才有意义:
- 执行
EXPLAIN发现明明有索引却走type: ALL(全表扫描),而key字段为空或不是预期索引 - 慢查询日志里反复出现某条语句,
rows估算值比实际匹配行数大几个数量级(比如估算 100 行,实际返回 10 万行) -
SHOW INDEX FROM table_name中某个字段的Cardinality长期为 0 或极低(如 1),但你知道该字段实际有很多不同值 - 刚执行过
OPTIMIZE TABLE或大量INSERT ... SELECT,但执行计划没变
ANALYZE TABLE 的坑:锁、耗时、无效重试
它不阻塞 DML(INSERT/UPDATE/DELETE),但会加一个表级读锁——意味着期间不能执行 DROP TABLE、ALTER TABLE 这类 DDL,也不能对同一张表并发执行另一个 ANALYZE TABLE。
大表执行时间不可控:采样页越多越准,但也越慢;某些版本在 READ-COMMITTED 隔离级别下,还可能因 MVCC 版本链太长导致卡住。
- 别在业务高峰跑,尤其对千万级以上单表
- 避免脚本里“失败就重试”,
ANALYZE TABLE返回成功不代表 Cardinality 已更新——要查SHOW INDEX确认 - 如果用了分区表,
ANALYZE TABLE默认只分析元数据,需显式指定分区名,如ANALYZE TABLE t PARTITION(p2024) - Percona Server 或 MariaDB 用户注意:
ANALYZE TABLE行为略有差异,比如支持INPLACE模式,但标准 MySQL 不支持
比 ANALYZE 更稳的替代方案
如果发现 ANALYZE TABLE 反复不准,说明采样机制本身已扛不住你的数据特征。这时候得换策略:
- 强制让优化器信任某个索引:
SELECT * FROM t USE INDEX (idx_status),绕过 Cardinality 判断 - 升级到 MySQL 8.0+ 并开启直方图:
ANALYZE TABLE t UPDATE HISTOGRAM ON status;,它记录值分布而非简单基数,对范围查询更友好 - 对固定模式的慢查,用
OPTIMIZER_HINT(如/*+ USE_INDEX(t idx_created_at) */)固化执行计划 - 真正根治?检查索引设计本身——Cardinality 低往往因为字段区分度差(比如
is_deleted TINYINT只有 0/1),这种字段单独建索引意义不大
Cardinality 不准从来不是孤立问题,它只是数据分布、索引设计、采样机制三者共同暴露的信号。盯着那个数字调,不如先看 EXPLAIN 输出里优化器到底为什么放弃你的索引。


















