直接回答:选择性 = 不同值数 / 总行数,越接近 1 索引越能“一捞一条”。优化器基于代价估算:用索引要“索引扫描 + 回表随机读”,估算行数超过表的一定比例(经验 5%~20%,存储与缓存状况相关)时全表顺序扫反而便宜——低选择性索引(如性别、状态位)被忽略是理性决策,不是优化器犯傻。

展开解析:定量直觉:表 100 万行,status 只有 3 个值,status='x' 命中 33 万行——回表 33 万次随机 IO 远超全表顺序扫加内存过滤。优化器的依据是统计信息(PG 的 pg_stats 的 n_distinct 与 most_common_vals、MySQL 的 cardinality 采样),统计过期会导致误判——大表批量变更后 ANALYZE 是常规运维。低选择性列的正确用法:不当独立索引,当复合索引的前缀或后缀——(tenant_id, status) 里 status 起过滤加速作用;部分索引(PG 的 partial index)——只为 WHERE status='pending' 的 1% 行建索引,选择性被人为构造出来,冷热分明场景的利器;倒排/位图——数仓的 bitmap 索引(Greenplum)才是低选择性的原生解法。实战排查流程:慢查询 EXPLAIN 发现没用索引——先看预估行数与实际行数的偏差(stats 过期?),再看选择性本身(该列真的过滤得动吗?),最后看回表成本(覆盖索引能不能消掉——把查询列都塞进索引)。反直觉案例:高选择性索引也可能被忽略——参数化查询的 plan 缓存按通用估算走(PG 的 generic plan)、LIKE 前缀通配、对列做函数运算使统计失效。收束:索引是给优化器的“路径选项”,不是命令——理解代价模型才能与它共谋。

追问方向:覆盖索引为什么能改变代价天平?复合索引的列序与选择性的关系?

(约 490 字)