前缀索引必须验证区分度、匹配查询模式,并接受不支持排序和覆盖扫描的代价;判断是否适用只需看查询是否总用前缀匹配及前N字符是否足够“认人”。

直接说结论:前缀索引不是“能建就建”,而是必须验证区分度、匹配查询模式、并接受它不支持排序和覆盖扫描的代价。对海量数据表,盲目加 email(10) 这类固定长度前缀,大概率白忙活,甚至拖慢写入。
怎么判断这个VARCHAR字段适合加前缀索引
只看两点:查询是否总用前缀匹配,以及字段前N个字符是否足够“认人”。
- 典型适用场景:
WHERE title LIKE 'MySQL优化%'、WHERE email LIKE 'admin@%'、WHERE url LIKE 'https://example.com%'—— 这些能走前缀索引;但LIKE '%login'或WHERE SUBSTRING(email, -3) = 'com'完全无效 - 区分度必须实测:执行
SELECT COUNT(DISTINCT LEFT(email, 8)) / COUNT(*) AS sel FROM users;,结果低于 0.9 就说明前8位太短;高于 0.95 且比LEFT(email, 6)提升不到 0.01,那6位就够了 - 特别注意编码:UTF8MB4 下一个中文占 4 字节,
email(10)是 10 字符,但name(10)若存中文,实际可能只截了 2–3 个字 —— 建议先用CHAR_LENGTH()和LENGTH()对比样本
如何选准前缀长度N而不是拍脑袋定10或20
不能靠经验,得用数据驱动。重点不是“最长要多少”,而是“最短够不够用”。
- 先算完整列选择性:
SELECT COUNT(DISTINCT email) / COUNT(*) FROM users; - 再横向对比几个候选长度:
SELECT COUNT(DISTINCT LEFT(email, 5)) / COUNT(*) sel5, COUNT(DISTINCT LEFT(email, 6)) / COUNT(*) sel6, COUNT(DISTINCT LEFT(email, 7)) / COUNT(*) sel7 FROM users; - 选那个首次达到完整选择性 90% 以上的最小 N。比如
sel6 = 0.91,sel5 = 0.72,那就用 6 —— 多建 1 字符,索引体积可能涨 15%,却没带来实质收益 - 对超长字段(如
TEXT或VARCHAR(1000)),建议从 N=8 开始试,逐步加到 16、24,超过 32 很少有边际收益
建索引时容易忽略的语法和行为细节
MySQL 的前缀索引语法看着简单,但几个关键点一错就失效。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 建法只有两种合法:
CREATE INDEX idx_u_email ON users(email(8));或ALTER TABLE users ADD INDEX idx_u_email(email(8));——ADD INDEX email(8)少了括号外的索引名,会报错 - 不支持函数索引变体:不能写
email(8) ASC,也不能在前缀索引上加USING BTREE(默认就是) -
SHOW CREATE TABLE users里看到的是KEY `idx_u_email` (`email`(8)),注意括号在列名后、逗号前,这是识别前缀索引的标志 - 一旦建了前缀索引,
EXPLAIN中的key_len值会变小(比如原来是 303,现在是 35),这是正常现象,不代表索引没生效
为什么加了前缀索引,EXPLAIN还是显示type=ALL
常见原因不是语法错,而是查询条件没对上索引能力边界。
- 用了
OR:比如WHERE email LIKE 'a%' OR status = 'active',即使email有前缀索引,也可能被优化器放弃 - 隐式类型转换:
WHERE email = 12345(email 是字符串),触发全表扫描 —— 检查EXPLAIN的type和possible_keys是否为空 - 前缀长度不够匹配实际查询:比如索引是
email(6),但查询是WHERE email LIKE 'admin@domain.com%',前6字符是'admin@',没问题;但如果查'administrator@%',前6是'admini',而库里大量邮箱都以'admini'开头,区分度崩了,优化器宁愿全扫 - 表统计信息过期:执行
ANALYZE TABLE users;更新索引基数,尤其在批量导入后
真正难的不是建索引的动作,而是确认你的查询模式、数据分布、索引长度三者是否咬合。一个 email(8) 在用户表里有效,在日志表里可能完全失效 —— 因为后者邮箱字段大量重复前缀(如全是 noreply@)。别跳过 LEFT() 统计这一步,它花不了两分钟,但省下的是后续几小时的排查时间。










