首页/数据/SQL 速查/基数与区分度:索引值不值得
性能与执行计划72 条

基数与区分度:索引值不值得

Cardinality 是索引列不同值的估算数:区分度低(性别、状态)的单列索引帮助有限,组合列或改写更有效。

SQL 速查数据更新于 2026-10-13

基数与索引值不值得建

基数指的是索引列上不同取值的估算个数,区分度是基数与总行数的比值。这个比值决定索引能不能有效缩小扫描范围:区分度接近一(几乎每行都不同)的列做索引前导列效果最好;只有几个取值的列,比如性别、状态、是否删除,单列索引往往扫到的行占了整表一大半,优化器宁可直接全表扫描。判断某列该不该建索引,先看它的实际数据分布,而不是看字段名像不像查询条件。

基本信息

适用平台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';

区分度与索引效果

区分度典型列索引效果
接近一主键、订单号、手机号扫描范围可缩小到极少行,效果最好
中等用户编号、商户编号效果取决于过滤后的行数占比
很低状态、性别、是否删除单列索引常被放弃,适合放进复合索引
分布倾斜大多数行同一取值、少数行不同要按最常用的取值分布评估,看平均无意义
基数只是估算值:采样统计受采样比例与数据变化影响,可能明显偏离真实情况。用非常小的表测试区分度没有意义,样本量太小统计信息本身就不稳定。
低区分度列不是不能建索引:当查询总是与另一个高区分度列同时出现时,把它放在复合索引的后面能起到过滤作用,比单独建索引有价值得多。

易错点

  • 把估算基数当精确值使用,据此下结论删索引或建索引,可能建立在失真的统计上
  • 给每个过滤列都建单列索引:低区分度列效果有限,索引维护成本却持续增加
  • 复合索引把低区分度列放在最前面,后续列几乎无法被有效利用
  • 只看字段名判断适用性,不看真实分布,忽略了数据倾斜带来的估算偏差
  • 数据量增长后不刷新统计信息,基数长期停留在旧值,优化器持续做出错误选择

数据来源与参考

  • MySQL 8.0 Reference Manual §8.3.1 How MySQL Uses Indexes
  • PostgreSQL Documentation §14.2 Statistics Used by the Planner
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌