前缀索引长度须依数据区分度确定,目标是用最短字符数覆盖绝大多数不同值;区分度=前n字符的不同值数/总行数,≥0.9可接受,高要求≥0.99;需结合字符集字节限制与查询模式(仅左对齐匹配有效)。

前缀索引长度不能靠经验拍脑袋定,必须基于字段真实数据的区分度来选——目标是用最短的字符数,覆盖绝大多数不同值。
先算区分度,看数据说了算
区分度 = 字段前 N 个字符的不同值数量 ÷ 总行数。越接近 1 越好,通常 ≥0.9 就可接受,高要求场景建议 ≥0.99。
- 查整列实际区分度:SELECT COUNT(DISTINCT email) / COUNT(*) FROM users;
- 从短到长试前缀:比如依次执行
SELECT COUNT(DISTINCT LEFT(email, 5)) / COUNT(*) FROM users;
SELECT COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) FROM users;
SELECT COUNT(DISTINCT LEFT(email, 15)) / COUNT(*) FROM users; - 观察比值跃升点:若从 0.72 → 0.91 → 0.985,说明 15 是关键拐点;再试 16 若只升到 0.987,就没必要继续加了
注意字符集对字节上限的硬约束
你写的 (15) 是字符数,但 InnoDB 存储索引按字节算。utf8mb4 下 1 个中文/emoji 占 4 字节,所以 15 字符最多占 60 字节;而 MySQL 5.7+ 默认单列前缀索引上限是 3072 字节(即最多支持 FLOOR(3072/4)=768 字符),但实际中区分度早饱和了,根本用不到那么长。
- gbk 字符集:1 字符 = 2 字节 → (100) 最多占 200 字节
- utf8(非 utf8mb4):1 字符 ≤ 3 字节 → (100) 最多占 300 字节
- 别踩坑:设 email(200) 在 utf8mb4 下理论需 800 字节,虽未超限,但很可能前 20 位已足够区分,白白浪费空间
验证是否真能命中查询
前缀索引只对 左对齐匹配 有效,比如 WHERE email LIKE 'zhang%' 或 WHERE name = 'LiMing'(等值查询时会先走前缀索引定位候选行,再回表校验全值)。
- ✅ 有效:WHERE sname LIKE '王%'、WHERE address LIKE '上海市徐汇区%'
- ❌ 失效:WHERE sname LIKE '%明'、WHERE email LIKE '%@gmail.com'、WHERE SUBSTR(email, -3) = 'com'
- 建完务必用 SHOW INDEX FROM users; 确认 Key_len 显示的是你设定的字符数(如 15),不是 0 或其他异常值
实操加索引的写法
已有表加前缀索引用 ALTER TABLE,语法简洁:
- ALTER TABLE users ADD INDEX idx_email_pre15 (email(15));
- ALTER TABLE articles ADD INDEX idx_title_pre20 (title(20));
- TEXT 类型字段必须指定长度,否则报错 ERROR 1170
- 大表操作建议在低峰期执行,避免长时间锁表











