基数与索引值不值得建
基数指的是索引列上不同取值的估算个数,区分度是基数与总行数的比值。这个比值决定索引能不能有效缩小扫描范围:区分度接近一(几乎每行都不同)的列做索引前导列效果最好;只有几个取值的列,比如性别、状态、是否删除,单列索引往往扫到的行占了整表一大半,优化器宁可直接全表扫描。判断某列该不该建索引,先看它的实际数据分布,而不是看字段名像不像查询条件。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| MySQL 查看方式 | 用索引查看语句读取估算基数,需要统计信息新鲜 |
| PostgreSQL 查看方式 | 统计信息视图里记录不同取值估算数 |
| 更新方式 | 重新采样统计信息,MySQL 的采样页数可配置 |
| 判定标准 | 基数与总行数的比值,越接近一越适合做前导列 |
典型使用场景
- 建索引前评估:某个过滤条件列到底值不值得单独建索引
- 复合索引排序:决定把哪一列放在最前面
- 排查索引失效:确认是优化器放弃索引还是条件写得不匹配
- 统计信息刷新:数据大幅变化后重新采样,让估算贴近现实
- 索引精简:识别长期低效的单列索引并合并进复合索引
查看与更新基数
查看与更新基数
-- MySQL:查看索引的估算基数(统计信息采样结果)
SHOW INDEX FROM orders;
-- 让统计信息重新采样,基数随之刷新
ANALYZE TABLE orders;
-- 低区分度列:状态字段只有几个取值,单列索引收益有限
SELECT * FROM orders WHERE status = 'paid';
-- 换成区分度更高的组合,前导列选过滤后剩余行更少的那个
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);
-- PostgreSQL:查看列的不同取值估算与分布
SELECT attname, n_distinct, most_common_vals
FROM pg_stats
WHERE tablename = 'orders';区分度与索引效果
| 区分度 | 典型列 | 索引效果 |
|---|---|---|
| 接近一 | 主键、订单号、手机号 | 扫描范围可缩小到极少行,效果最好 |
| 中等 | 用户编号、商户编号 | 效果取决于过滤后的行数占比 |
| 很低 | 状态、性别、是否删除 | 单列索引常被放弃,适合放进复合索引 |
| 分布倾斜 | 大多数行同一取值、少数行不同 | 要按最常用的取值分布评估,看平均无意义 |
基数只是估算值:采样统计受采样比例与数据变化影响,可能明显偏离真实情况。用非常小的表测试区分度没有意义,样本量太小统计信息本身就不稳定。
低区分度列不是不能建索引:当查询总是与另一个高区分度列同时出现时,把它放在复合索引的后面能起到过滤作用,比单独建索引有价值得多。