Prompts-MCP 686 skills · ai / design / design-pattern / framework / fundamentals / habit / lang / tech-selection /mcp/sse
framework framework/mysql/index/cardinality-and-prefix.md medium

mysql-cardinality-and-prefix

索引区分度与前缀索引 — 低区分度列(状态/性别/软删标记)单独建索引收益低,长字符串列用前缀索引控制索引体积。Use when 纠结某列该不该建索引 / 给长字符串列建索引 / 评审冗余索引时。

区分度选择性cardinalityselectivity前缀索引prefix index冗余索引
paths
  • *.sql
  • *.xml
  • *.java

MySQL · 区分度与前缀索引

本条只管「这列该不该建索引、字符串太长怎么建」。建了为什么没走见 index-fail-cases.md;联合索引列顺序见 leftmost-prefix.md

区分度(选择性)

选择性 = 不重复值数 / 总行数,越接近 1 越值得建索引。

-- 估算某列选择性,> 0.1 才考虑单独建索引
SELECT COUNT(DISTINCT status) / COUNT(*) FROM orders;
列类型 选择性 建索引建议
主键 / 唯一键 =1 必建(天然唯一)
user_id / order_no 适合
status / type(少量枚举) 不单独建,放联合索引靠前的等值列里才有意义
is_deleted / 性别(值仅 2-3 种) 极低 不建,优化器多半放弃;用联合索引覆盖

低区分度列的正确用法:作为联合索引最左等值列与高区分度列组合,而非单列索引。

前缀索引

长字符串列(URL、邮箱、长 token)整列建索引太占空间,可只索引前 N 个字符:

-- 取能保住足够区分度的最短前缀长度
SELECT COUNT(DISTINCT LEFT(email, 8)) / COUNT(*) FROM users;   -- 试不同长度
ALTER TABLE users ADD INDEX idx_email_prefix (email(12));      -- 选区分度够的长度
说明
长度选取 取使选择性接近全列的最短前缀,平衡区分度与体积
代价 前缀索引无法用于覆盖索引和 ORDER BY(只有前缀信息)
不适用 区分度集中在尾部的字符串(如同域名邮箱),前缀几乎不区分

反例

-- ❌ 给只有 0/1 的 is_deleted 单独建索引:优化器基本不用,白占空间拖写入
ALTER TABLE orders ADD INDEX idx_deleted (is_deleted);

-- ❌ 已有 (a,b) 联合索引又单独建 (a):(a) 冗余,删掉
ALTER TABLE t ADD INDEX idx_a (a);   -- 被 idx_ab 的最左前缀覆盖

自检

  • [ ] 这列选择性是否够高(粗判 > 0.1)才单独建索引?
  • [ ] 低区分度列是否改为放进联合索引最左等值位,而非单列索引?
  • [ ] 长字符串列是否用前缀索引、并验证过前缀长度的区分度?
  • [ ] 是否存在被联合索引最左前缀覆盖的冗余单列索引?

相关