Cardinality不准不会直接导致索引失效,但会严重误导优化器放弃本该有效的索引;其本质是采样估算偏差(如默认20页)使优化器误判选择性,表现为EXPLAIN中key为NULL、type为ALL、rows接近总行数,需通过ANALYZE TABLE更新统计并结合COUNT(DISTINCT col)验证。

Cardinality不准本身不会直接让索引“失效”,但它会严重误导优化器,导致它主动放弃使用本该有效的索引。
Cardinality不准时,EXPLAIN 显示 key 为空但实际有索引
MySQL 的查询优化器依赖 Cardinality(基数)估算索引的选择性——即“这个索引大概能过滤掉多少行”。如果统计值严重偏低(比如性别字段 state 的 Cardinality 被误报为 2,而实际表中只有两个值),优化器就会认为该索引“区分度太差”,不如全表扫描快,于是跳过它。
典型现象:
-
EXPLAIN中possible_keys列有索引名,但key列为NULL -
type是ALL或index,而非ref/range - 执行时间明显变长,
rows值接近总行数
什么情况下 Cardinality 会不准
MySQL 默认通过采样页(innodb_stats_sample_pages,默认 20)估算基数,不是全量扫描,因此容易失真。以下场景尤其常见:
- 表刚导入大量数据,但没触发自动统计更新(
innodb_stats_auto_recalc=OFF或数据变更未达阈值) - 表被频繁
TRUNCATE后快速插入新数据,旧统计残留 - 联合索引中前导列基数极低(如
(status, created_at),status只有 'active'/'inactive'),采样偏差放大 - 使用
MEMORY引擎表,或临时表,统计信息不持久
如何验证和修复不准的 Cardinality
先用 SHOW INDEX FROM table_name 查看当前 Cardinality 值;再用 SELECT COUNT(DISTINCT col) 手动核对真实基数。若偏差大,手动更新统计:
- 强制刷新单表:
ANALYZE TABLE users - 刷新整个库(谨慎):
mysqlcheck -a -u root -p database_name - 调整采样精度(需重启或动态设置):
SET GLOBAL innodb_stats_sample_pages = 100
注意:ANALYZE TABLE 是 DDL 操作,会对表加读锁(InnoDB 行级锁下影响较小,但大表仍需避开高峰)。
Cardinality 不准 + 隐式转换 = 双重失效
这是最容易被忽略的组合坑:比如字段是 VARCHAR(32),但查询写成 WHERE status = 0(数字 0),MySQL 先做隐式转换,再发现 Cardinality 统计值又偏低,优化器直接判定“这索引既难用、又没用”,两步放弃。
真正关键的不是“索引是否存在”,而是“优化器是否相信它值得用”——而 Cardinality 就是它做判断时最常偷懒参考的数字。


















