前缀索引是显式指定字符串字段前N个字符参与索引构建,而非截断字段;其核心作用是控制索引键字节长度不超限(如utf8mb4下767字节→最多191字符),需按字符集动态计算安全长度,并验证选择性以确保查询有效。

前缀索引不是“截断字段”,而是显式指定索引覆盖的字节数
很多人误以为 INDEX idx_name(name) 是对整个 name 字段建索引,其实只要该字段是 VARCHAR 且长度 >191(utf8mb4 下),MySQL 就会按最大可能字节数计算索引键长 → 直接触发 Specified key was too long; max key length is 767 bytes。前缀索引的本质是告诉 MySQL:“只用前 N 个字符参与索引构建”,从而把字节上限控在 767 以内。
怎么算出安全的前缀长度?别硬背 191,要动态算
关键不是字符数,是字节数。utf8mb4 下每个字符最多占 4 字节,所以安全前缀长度 = floor(767 ÷ 4) = 191;但如果你用的是 latin1(1 字节/字符),那就能用到 767;gbk 是 2 字节/字符 → 最多 383。实际操作时必须查表字符集:
- 执行
SHOW CREATE TABLE `your_table`,确认字段字符集和排序规则 - 若字段定义为
VARCHAR(255) CHARACTER SET utf8mb4,则前缀长度不能超过191 - 若字段是
TEXT或VARCHAR(500),也一样按字符集换算,不是看括号里那个数字
ALTER TABLE 加前缀索引时,语法必须带长度
直接写 ADD INDEX idx_token(token) 依然会报错,因为 MySQL 默认尝试索引全字段。必须显式指定前缀长度:
ALTER TABLE users ADD INDEX idx_token(token(191));
注意几个易错点:
-
token(191)中的191是字符数,不是字节数 —— MySQL 内部会自动乘以字符集单字符最大字节数 - 联合索引也要分别控制每列前缀:比如
(email(191), name(100)),总字节数 = 191×4 + 100×4 = 1164 → 仍超 767,此时得再砍,比如email(100), name(50) - 前缀长度不能超过字段定义长度,
VARCHAR(100)上写token(191)不报错但会被自动截断为 100
前缀索引能解决报错,但不保证查询效率
加了 token(191) 后建索引成功了,不代表所有 WHERE token = ? 查询都能走索引。如果实际值前 191 字符都相同(比如 UUID 前缀高度重复),就会大量回表或全表扫描。
- 先用
SELECT COUNT(DISTINCT LEFT(token, 191)) / COUNT(*) FROM users;看选择性是否 > 0.9 - 低选择性时,考虑改用哈希字段(如
token_hash CHAR(32) AS (MD5(token)) STORED)+ 精确索引 - 不要在
LIKE '%xxx'场景依赖前缀索引 —— 它只加速前缀匹配,后缀或中缀无效
真正卡住你的往往不是“能不能建”,而是“建了有没有用”。字节限制只是表层错误,背后是索引设计与数据分布的匹配问题。


















