cardinality是innodb通过采样估算的索引唯一值数量,非精确统计;其值影响优化器对选择性的判断(selectivity=cardinality/table_rows),进而决定是否使用索引。

MySQL 优化器不直接“计算”选择性,而是用 Cardinality 和 TABLE_ROWS 做一个简单除法:Selectivity = Cardinality / TABLE_ROWS。这个值越接近 1,说明索引区分度越高;越接近 0(比如 0.001),说明值高度重复,走索引可能不如全表扫描。
Cardinality 怎么来的?不是 count(distinct) 那种精确统计
它来自采样估算,不是全表扫描。InnoDB 默认只读取 innodb_stats_persistent_sample_pages 个随机索引页(8.0 默认是 20),再根据这些页里键值的分布推算整个索引的去重数量。
- 采样页数太少(比如默认 20)遇上数据倾斜严重时,
Cardinality可能严重偏低或偏高 - 例如
gender列实际只有 M/F 两个值,但某次采样恰好抽到全是 M 的页,就可能估算出Cardinality≈ 1 - 直方图(8.0+)不影响
Cardinality,只影响 WHERE 条件的过滤比例估算,比如status = 'paid'占多少行 - 修改采样页数需重启或执行
ANALYZE TABLE才生效,仅改参数不触发重算
为什么 SHOW INDEX 看到的 Cardinality 每次都不一样?
因为每次 ANALYZE TABLE 或自动更新都重新采样,而采样是随机的。哪怕表没变,两次结果也可能差一倍——这不是 bug,是设计使然。
- 自动更新由
innodb_stats_auto_recalc控制,默认 ON,当变更行数估算超约 1/16(≈6.25%)时触发 - 这个“1/16”本身也是个粗略计数器,不精确,且异步执行,可能延迟几分钟
- 如果表写入频繁(如每秒千级 INSERT),
Cardinality很可能长期滞后于真实分布 -
SHOW INDEX查的是mysql.innodb_index_stats表里的n_diff_pfx01字段,它就是采样后存下来的那个估算值
Cardinality 错了,优化器就会选错索引
这是最常被忽略的慢查询根源之一。优化器看到 Cardinality = 2 就认为 WHERE gender = 'M' 会返回一半数据,于是放弃索引、走全表扫描 —— 即使你刚加了覆盖索引也无济于事。
- 典型现象:
EXPLAIN显示type: ALL、key: NULL,但字段明明有索引 - 对比
SELECT COUNT(DISTINCT col)和SHOW INDEX里的Cardinality,差 3 倍以上基本可判定不准 - 临时修复:手动执行
ANALYZE TABLE table_name,强制重采样 - 长期方案:对关键大表,设
innodb_stats_persistent = ON+innodb_stats_auto_recalc = OFF,改为业务低峰期定时ANALYZE TABLE
真正麻烦的不是 Cardinality 不准,而是它不准时你根本不会想到去看它——往往先去调 SQL、加索引、改配置,绕一大圈才发现问题在统计信息本身。











