前缀索引长度必须基于实际区分度确定,而非经验猜测;需用sql验证count(distinct left(col,n))/count(*)≥0.9(高要求≥0.99),并结合字符集计算字节上限(如utf8mb4下n字符=4n字节),同时注意唯一索引对前缀长度更敏感,不支持like '%xxx'查询。

前缀索引长度不是猜出来的,得看字段实际区分度
直接设 name(20) 或 title(100) 很容易翻车——前 20 位全是 user_,或前 100 位才刚到商品型号编号,索引就形同虚设。核心判断依据只有一个:前缀能否把大多数不同值区分开。这个“大多数”,通常指 90%(比值 ≥ 0.9),高要求场景可压到 99%(≥ 0.99)。
验证必须用真实数据跑 SQL,不能靠经验拍脑袋:
SELECT COUNT(DISTINCT LEFT(name, 15)) / COUNT(*) FROM users;SELECT COUNT(DISTINCT LEFT(title, 40)) / COUNT(*) FROM products;
从短往长试(比如 10→20→30→40),观察比值跃升点。一旦从 0.82 跳到 0.91,说明 30 是个关键拐点;再试 35,如果只涨到 0.915,就没必要继续加了。
字符集和存储引擎决定字节上限,不是字符数
你设的 email(30),MySQL 实际存多少字节,取决于字符集:
-
utf8mb4下,一个中文/emoji 占 4 字节 → 30 字符 = 最多 120 字节 -
gbk下,一个中文占 2 字节 → 30 字符 = 最多 60 字节
而 InnoDB 对单列前缀索引有硬性字节限制:
- MySQL 5.6 默认:767 字节(
innodb_large_prefix=0) - MySQL 5.7+ 默认:3072 字节(
innodb_large_prefix=1)
所以 VARCHAR(255) 字段在 utf8mb4 下,理论最大前缀是 FLOOR(3072 / 4) = 768,但实际根本用不到这么长——区分度早饱和了,还白白撑大索引树。
唯一索引对前缀长度更敏感,稍超就报错
建 UNIQUE INDEX idx_email ON users (email(20)) 时,如果实际数据里存在 alice@example.com 和 alex@example.com,前 20 位都是 alice@example.com(假设截断后相同),那这俩值就被判为“重复”,INSERT 直接失败。
这种情况 MySQL 不会妥协:
- 无论
innodb_strict_mode开关,唯一索引超长或导致歧义,一律报错ERROR 1071 (42000): Specified key was too long或Duplicate entry - 非唯一索引则宽松些:strict mode 关闭时,会自动截断并 warning;开启时同样报错
所以对唯一性有要求的字段(如邮箱、用户名),前缀长度必须确保 COUNT(DISTINCT LEFT(col, n)) == COUNT(*),至少也得接近 0.99。
别忽略业务查询模式,前缀索引不支持 LIKE '%xxx'
前缀索引只加速 =、IN、BETWEEN 和 LIKE 'abc%' 这类能利用最左前缀的查询。如果你经常写 WHERE title LIKE '%手机%',前缀索引完全无效——它只存开头几个字符,没法反向匹配。
此时该考虑的不是加长前缀,而是:
- 改写查询:用全文索引(
FULLTEXT)或外部搜索引擎(Elasticsearch) - 确认是否真需要模糊查:很多“LIKE %xxx%”其实是前端筛选逻辑错误,应转为等值或范围查询
- 检查字段是否冗余:URL、HTML 片段这类长文本,99% 场景不该建索引
真正容易被忽略的是:前缀长度选对了,但字符集配置没同步更新——比如表用 utf8mb4,却按 utf8(最多 3 字节)估算字节数,结果上线后发现 title(100) 实际占 400 字节,离 3072 还远,但开发误以为快爆了,又缩回 50,反而让区分度掉到 0.7。字节账,必须按实际字符集算清楚。











