什么是索引的选择性(selectivity)
索引的选择性,指的是索引列中不同值的数量与该列总行数的比值,即
SELECT COUNT(DISTINCT col) / COUNT(*) FROM table。它的取值范围是 0 到 1:越接近 1,说明该列重复值越少,索引区分度越高;越接近 0,说明大量重复(比如性别、状态字段),索引效率越低。
为什么低选择性字段不适合单独建索引
MySQL 的 B+ 树索引在查找时依赖“跳过无关分支”的能力。如果
col有 95% 的值都是
'active',那么查询
WHERE col = 'active'仍需扫描全表约 95% 的叶子节点——此时走索引反而比全表扫描更慢(额外多了树遍历和回表开销)。 常见低选择性字段:
is_deleted、
status(只有 2–3 个枚举值)、
genderMySQL 优化器通常会拒绝使用这类索引,执行计划里
key字段为
NULL,
type是
ALL即使强制用
FORCE INDEX,实际性能往往更差
如何判断和测量某列的选择性
别只看直觉,用 SQL 算出来最准。注意:对大表要加
LIMIT或采样估算,避免全表扫描卡住。
SELECT COUNT(*) AS total, COUNT(DISTINCT user_id) AS distinct_uid, COUNT(DISTINCT email) AS distinct_email, ROUND(COUNT(DISTINCT email) / COUNT(*), 4) AS email_selectivity FROM users;email 选择性 ≈ 1.0?适合建唯一索引 user_id 是主键,天然 1.0,无需额外判断 如果
email_selectivity (即 1%),基本可认为不适合作为独立索引首列注意 NULL 值是否参与计算:
COUNT(DISTINCT col)默认忽略 NULL,但业务上 NULL 可能代表一类有效状态
低选择性字段怎么用索引才有效
单独建没用,但组合起来可能很关键。核心思路是:让低选择性字段作为联合索引的「后缀」,前面必须接高选择性列,且查询条件要满足最左前缀匹配。
错误写法:INDEX(status, created_at)→ 查询
WHERE status = 'draft'仍走不到索引 正确写法:
INDEX(user_id, status)→ 查询
WHERE user_id = 123 AND status = 'draft'可高效定位 再优化:
INDEX(user_id, status, updated_at)→ 支持覆盖查询,避免回表 注意顺序:高选择性列必须在前,否则索引失效;
status在前、
user_id在后,等于白建
真正容易被忽略的是:联合索引的“有效长度”取决于 WHERE 中连续出现的最左列数。哪怕你建了 5 列的索引,只要 WHERE 缺了第 2 列,后面全作废。
