前缀索引长度需通过区分度验证,区分度=count(distinct value)/count(),应≥0.95;需对比不同长度下区分度变化,选择收益饱和点;建唯一索引时须满足count(distinct left(col,n))=count();注意字符集与字节限制,且确保查询能命中索引。

前缀索引长度不是拍脑袋定的,必须用数据验证区分度;低于 0.95 的选择性通常不值得建。
怎么算当前字段的区分度
区分度 = COUNT(DISTINCT value) / COUNT(*),越接近 1 越好。先看整列:
SELECT COUNT(DISTINCT email) / COUNT(*) AS full_selectivity FROM users;
再对比不同前缀长度下的表现,比如试 5/8/10/12 位:
SELECT COUNT(DISTINCT LEFT(email, 5)) / COUNT(*) AS sel5, COUNT(DISTINCT LEFT(email, 8)) / COUNT(*) AS sel8, COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) AS sel10, COUNT(DISTINCT LEFT(email, 12)) / COUNT(*) AS sel12 FROM users;
- 结果里某个长度开始,区分度不再明显上升(比如从 0.94 到 0.95 只涨 0.01),就说明再加长度收益极低
- 如果
sel8 == sel10 == sel12,那直接选 8 就行,省空间 - 别只看平均值——用
MIN(LENGTH(email))确认最短值是否 ≥ 你打算取的长度,否则会截断空值或异常短数据
唯一索引对长度更敏感,稍超就报错
建 UNIQUE INDEX 时,MySQL 会严格校验前缀是否足以区分所有现有值。一旦发现两个不同原始值的前缀相同,就会报:ERROR 1071 (42000): Specified key was too long 或 Duplicate entry。
- 必须保证
COUNT(DISTINCT LEFT(email, n)) == COUNT(*),至少也要 ≥ 0.99 -
innodb_strict_mode开启时,非唯一索引超长也会报错;关闭时可能只 warning 并自动截断,但行为不可控 - 业务上常有“邮箱去重”逻辑,若前缀太短,INSERT 会意外失败,排查起来容易绕弯
字符集影响实际字节数,但语法单位仍是字符数
你在 CREATE INDEX idx ON t (email(10)) 里写的 10 是字符数,不是字节数。但 InnoDB 对单列索引有字节限制:
- MySQL 5.7+ 默认支持最长 3072 字节单列索引(
innodb_large_prefix=ON) - utf8mb4 下 1 个中文 = 1 字符 = 最多 4 字节 →
email(10)最坏占 40 字节,完全安全 - 但如果字段本身是
VARCHAR(255),又想取(200),就得确认200 × 4 = 800 ,否则建索引失败 - 别混淆
SHOW CREATE TABLE里显示的KEY长度(字符数)和SHOW INDEX里的Sub_part(也是字符数)
真正难的不是算数字,而是理解业务查询是否真能命中前缀索引——比如 WHERE email LIKE '%@qq.com' 完全用不上,而 WHERE email LIKE 'zhang%' 才行。上线前务必用 EXPLAIN 看 key_len 是否匹配你设的长度。











