cardinality不准不会报错但会导致优化器弃用索引,需通过对比cardinality与count(distinct col)是否差10倍以上来判断,再结合explain的rows和key验证,并排除隐式转换、函数包裹等干扰因素。

Cardinality不准本身不报错,但会悄悄让优化器放弃本该走的索引——所以不能只看SHOW INDEX返回的数字,得交叉验证。
查CARDINALITY和COUNT(DISTINCT col)是否差一个数量级
这是最直接的判断方式。优化器依赖Cardinality估算选择性,如果偏差过大(比如CARDINALITY = 120,但SELECT COUNT(DISTINCT city_id) FROM orders返回85000),基本坐实统计失真。
-
SHOW INDEX FROM orders里找city_id对应行的CARDINALITY值 - 执行
SELECT COUNT(DISTINCT city_id) FROM orders WHERE ...(WHERE条件要和实际查询一致,比如加AND status = 'paid') - 对比两者:差10倍以上就属于“离谱”,需要干预;差2–3倍属正常采样波动
看EXPLAIN中rows预估是否严重偏离实际
Cardinality不准最终会暴露在执行计划里。它不是让你怀疑“有没有索引”,而是让你质疑“优化器信不信这个索引”。
- 执行
EXPLAIN SELECT * FROM users WHERE city_id = 565 - 观察
rows列:如果实际只返回200行,但rows = 7800000,说明优化器认为这个条件几乎不过滤,根源大概率是city_id的Cardinality被低估 - 同时检查
possible_keys有值但key为NULL,且type = ALL或type = index,这是典型“有索引却不用”的信号
排除隐式转换、函数包裹等干扰项
别把优化器的合理放弃,当成统计错误。很多“Cardinality不准”的假警报,其实源于查询写法本身让索引失效。
- 字段是
VARCHAR,但写成WHERE city_id = 565(传数字)→ 触发隐式转换,索引失效,ANALYZE TABLE毫无意义 - 写成
WHERE DATE(created_at) = '2024-01-01'→ 函数导致索引无法下推,Cardinality再准也白搭 - 表用的是
MyISAM引擎 →ANALYZE TABLE有效,但innodb_stats_persistent等参数不适用,得换思路 -
innodb_stats_persistent = OFF且刚批量导入数据 → 统计根本没更新过,此时CARDINALITY就是旧的,不是不准,是“过期”
确认information_schema.STATISTICS里的CARDINALITY是否真变了
执行完ANALYZE TABLE后,别只信SHOW INDEX的时间戳。有些情况下值没变,只是UPDATE_TIME刷新了。
- 查
SELECT CARDINALITY FROM information_schema.STATISTICS WHERE TABLE_NAME = 'users' AND COLUMN_NAME = 'city_id' - 对比执行
ANALYZE TABLE前后的结果,必须看到数值变动,才算生效 - 如果值不变,检查是否被
FORCE INDEX硬编码覆盖,或者存在更优的复合索引(比如(city_id, status))让优化器主动跳过了单列索引
真正难的不是发现Cardinality不准,而是区分它是“采样失真”还是“查询写法自废武功”。每次怀疑之前,先盯住EXPLAIN的rows和key,再回溯到COUNT(DISTINCT),最后才动ANALYZE TABLE——漏掉任一环,都可能白忙活。











