前缀索引最优长度需基于真实数据区分度计算,即通过SQL测试COUNT(DISTINCT LEFT(col,n))/COUNT(*)比值,选择性达90%~95%且边际提升<0.5%的最小n值,并兼顾utf8mb4字节限制及查询模式限制。

前缀索引不是“能省多少就设多少”,而是必须用真实数据算出区分度拐点,否则要么查不准、要么白占空间。
怎么算出真正有效的前缀长度
核心看 COUNT(DISTINCT LEFT(col, n)) / COUNT(*) 这个比值是否稳定逼近全列选择性。不能靠经验或固定值(比如统一设 email(20))。
- 先跑完整列选择性:
SELECT COUNT(DISTINCT email) / COUNT(*) FROM users; - 再横向对比多个长度:
SELECT COUNT(DISTINCT LEFT(email, 8)) / COUNT(*) AS sel8, COUNT(DISTINCT LEFT(email, 12)) / COUNT(*) AS sel12, COUNT(DISTINCT LEFT(email, 16)) / COUNT(*) AS sel16 FROM users; - 选“边际提升 n:比如
sel12 = 0.942、sel16 = 0.945,那就选 12,不是 16 - 中文字段要按字节算:
utf8mb4下一个汉字占 4 字节,LEFT(email, 12)可能只截了 3 个汉字,得用CHAR_LENGTH()验证实际字符数 - 过滤异常值:如果字段里有大量空值或极短字符串(如
'a@b.c'),会拉低比值,得加WHERE email != '' AND CHAR_LENGTH(email) > 5再算
建索引时容易踩的语法和类型坑
CREATE INDEX 或 ALTER TABLE ... ADD KEY 时,BLOB/TEXT 字段不写长度直接报错;VARCHAR 虽然允许不写,但没指定长度就等于建完整索引,起不到压缩效果。
- BLOB/TEXT 必须显式指定长度:
ALTER TABLE logs ADD KEY idx_content(content(255));,否则报ERROR 1170 (42000): BLOB/TEXT column 'content' used in key specification without a key length - VARCHAR 字段漏写括号长度,比如写成
ADD KEY idx_name(name),MySQL 默认按全字段建索引,不是前缀索引 - 长度单位是“字符数”,不是字节数 —— 但
utf8mb4下 1 字符 = 1~4 字节,实际占用索引页空间仍受字节限制(InnoDB 单列索引前缀最大 767 字节,或 3072 字节开启innodb_large_prefix) - 别在随机性差的字段上硬套短前缀:比如所有 URL 都以
'https://'开头,url(8)的区分度几乎为 0
哪些查询能走前缀索引,哪些根本用不上
前缀索引只对“左匹配”生效,且无法支撑需要完整值的逻辑。不是写了 LIKE 就能用,也不是所有 WHERE 条件都受益。
- 能走索引:
WHERE name LIKE 'Li%'、WHERE email LIKE 'admin@%' - 完全失效:
WHERE name LIKE '%son'、WHERE email LIKE '%gmail.com'、WHERE SUBSTRING(email, -3) = 'com' - 隐式失效(看似能走,实际退化):
ORDER BY name、GROUP BY city、SELECT name FROM users WHERE name LIKE 'Li%' —— 无法覆盖扫描,必须回表取完整 <code>name值 - 注意 collation 影响:如果字段用
utf8mb4_0900_as_cs,大小写敏感,LEFT()计算时也按该规则区分,测试时要保持一致
前缀索引不是一建永逸的方案——数据分布变了、业务查询模式变了、新导入一批低区分度数据,原来最优的长度就可能变钝。定期重跑 LEFT() 区分度 SQL 比维护一张静态配置清单更可靠。


















