基数为什么重要
索引基数(cardinality)是索引列上不重复值的估算个数,直接决定选择性:基数越接近表行数,一次索引查找定位到的行越少,优化器就越愿意走这个索引。它是采样估算而非精确值,在数据大比例变更后容易失真,失真时优化器可能选错索引甚至放弃索引,表现为「索引明明存在却全表扫描」。
基本信息
| 适用平台 | MySQL 5.7 / 8.0(InnoDB 持久化统计信息) |
|---|---|
| 查看方式 | SHOW INDEX FROM tbl 的 Cardinality 列 |
| 刷新方式 | ANALYZE TABLE tbl |
| 增强手段 | 8.0 支持 ANALYZE TABLE ... UPDATE HISTOGRAM |
| 相关参数 | innodb_stats_persistent、innodb_stats_persistent_sample_pages |
典型使用场景
- 新建索引后确认优化器是否采纳,避免出现无效索引
- 大比例数据变更或不均匀导入后刷新统计信息,稳定执行计划
- 排查同一 SQL 时而快时而慢:怀疑统计信息漂移导致计划跳变
- 评估复合索引首列的选择性,判断列序是否合理
- 为倾斜分布的列建立直方图,改善选择性差的过滤条件
查看与刷新
查看与刷新
-- 查看索引基数与列序
SHOW INDEX FROM appdb.orders\G
-- 大比例变更后刷新统计信息
ANALYZE TABLE appdb.orders;
-- 8.0 为倾斜列建立直方图
ANALYZE TABLE appdb.orders UPDATE HISTOGRAM ON status WITH 64 BUCKETS;
-- 查看已建直方图
SELECT * FROM information_schema.column_statistics WHERE table_name = 'orders';
-- 用执行计划复核索引是否真的被使用
EXPLAIN SELECT * FROM appdb.orders WHERE status = 'paid' AND user_id = 42;如何判断基数是否可用
| 观察点 | 正常表现 | 异常表现 |
|---|---|---|
| Cardinality 与行数比 | 接近 1 表示选择性好 | 远小于行数说明区分度差 |
| 与真实去重数对比 | 同数量级 | 差几个数量级说明估算失真 |
| 执行计划 | 按预期走目标索引 | 显示全表扫描却又有该索引 |
| 列序读数 | Seq_in_index 与实际查询条件一致 | 首列用不上导致后续列被跳过 |
统计信息的目标不是精确,而是让优化器做出接近最优的选择;追求精确的代价(每次全量采样)往往高于收益。
不要为了刷新基数频繁对全库执行 ANALYZE TABLE:大表上采样会带来可观 IO,应按热点表按需执行。