前缀索引长度必须基于数据实际区分度动态确定,核心是计算选择性(COUNT(DISTINCT LEFT(col,n))/COUNT(*)),目标达0.9–0.99且取最小有效n;需结合字符集字节限制(如utf8mb4下1中文=4字节)和查询覆盖能力综合权衡。

前缀索引不是“设个长度就行”,它本质是在区分度和存储开销之间做动态取舍——选短了查不准,选长了白浪费,必须用数据说话。
怎么算出真正有效的前缀长度
不能拍脑袋定 10 或 20,得看字段实际分布。核心指标是选择性(distinct rate):越接近 1,说明前缀越能唯一标识整行。
- 先查完整字段的选择性:
SELECT COUNT(DISTINCT email) / COUNT(*) FROM users; - 再横向对比不同长度的前缀:
SELECT COUNT(DISTINCT LEFT(email, 5)) / COUNT(*) AS sel5, COUNT(DISTINCT LEFT(email, 6)) / COUNT(*) AS sel6, COUNT(DISTINCT LEFT(email, 7)) / COUNT(*) AS sel7 FROM users; - 目标不是“最大”,而是“拐点”:比如 sel6=0.92、sel7=0.955、sel8=0.956,那 7 就是性价比拐点——再加 1 字节提升微乎其微,但索引体积可能涨 15%。
中文字段前缀长度要按字节算,不是字符数
utf8mb4 编码下,一个汉字占 4 字节,name(10) 表示只索引前 10 字节,可能连一个汉字都存不下(如“张三”就占 8 字节,“王者荣耀”直接超)。这会导致索引失效或大量重复前缀。
- 查字段实际字节长度分布:
SELECT MAX(LENGTH(name)) FROM users; - 测试时用
LEFT(name, N)要配合LENGTH()理解真实覆盖范围,别只看CHAR_LENGTH() - 稳妥做法:对中文名字段,前缀长度至少设为 12~15(覆盖 3~4 个汉字),再结合选择性验证
哪些查询能用上前缀索引,哪些根本不行
前缀索引只支持最左前缀匹配,且仅限某些操作类型。不是建了就能加速所有含该字段的查询。
- ✅ 可用:
WHERE title LIKE 'MySQL优化%'、WHERE email = 'a@b.com'(等值查询会先走前缀索引,再回表校验全值) - ❌ 不可用:
ORDER BY title(索引不含完整值,无法排序)、GROUP BY title(同理)、SELECT title FROM ... WHERE ...(无法覆盖索引,必回表) - ⚠️ 危险:
WHERE title LIKE '%优化'(后缀匹配,索引完全失效)
什么时候该放弃前缀索引,换别的方案
当前缀长度逼近字段平均长度(比如 VARCHAR(255) 的字段,最优前缀要设到 200+),或者业务需要 ORDER BY/GROUP BY/覆盖查询时,前缀索引就失去了意义。
- 考虑哈希列替代:新增
email_hash CHAR(32)存 MD5,建普通索引,适合等值查询 - 考虑生成式前缀:如 URL 字段统一去掉
https://再截取,避免前缀全是重复协议头 - 千万级表上频繁执行
LIKE '%keyword%'?别硬扛,该上 Elasticsearch 就上
真正容易被忽略的是数据漂移——今天选 7 刚好,半年后新注册用户邮箱前缀集中度上升,选择性掉到 0.88,查询就开始变慢。前缀长度不是一劳永逸的配置项,而是需要定期用 ANALYZE TABLE + 选择性重测来校准的活参数。



















