mysql索引键长度上限为3072字节(5.7+默认),按字节而非字符计算,utf8mb4下汉字占4字节,超限即报错;联合索引各列字节累加,text/blob须指定前缀长度,前缀过短降低区分度,过长增加i/o,且不支持完整字段排序与函数查询。

MySQL 对索引键长度有硬性限制
MySQL 的 InnoDB 引擎对单个索引的键长度(index key length)有明确上限:默认是 3072 字节(5.7+ 版本),老版本如 5.6 是 767 字节。这个限制不是建议值,而是存储引擎在创建索引时直接校验的硬边界——超了就报错 Specified key was too long; max key length is 3072 bytes。
关键点在于:这个长度按**字节算,不是字符数**。比如 utf8mb4 编码下,一个汉字最多占 4 字节,那一个 VARCHAR(1000) 字段理论最大键长就是 1000 × 4 = 4000 字节,直接超限。
- 建联合索引时,各列长度累加计算,只要总和超过
3072就失败 -
TEXT、BLOB类型字段不能直接全字段建索引,必须指定前缀长度(如content(255)) - 即使字段本身是
VARCHAR(200),若用了utf8mb4_unicode_ci排序规则,InnoDB 仍可能按最大可能字节数预估,导致误判超限
大字段建索引会触发前缀索引截断
当你对 VARCHAR(2000) 或 TEXT 列执行 CREATE INDEX idx_content ON t(content),MySQL 不会报错,而是自动转为前缀索引——但前提是显式指定长度,否则直接拒绝。隐式行为不存在。
真正执行时,优化器会按你写的前缀长度去构建 B+ 树节点。比如 content(191) 在 utf8mb4 下刚好不超 767 字节(191×4=764),这是很多旧迁移脚本里反复出现的 magic number。
- 前缀太短(如
content(10)):区分度低,大量重复前缀,索引失效概率高 - 前缀太长(如
content(1000)):单个索引页能存的键值变少,树变高,IO 次数上升 - 无法用于
ORDER BY content或GROUP BY content—— 前缀索引不支持完整字段排序
执行计划中看不到“被截断”的提示
用 EXPLAIN 查看带前缀索引的查询,key_len 显示的是实际使用的字节数(如 764),但不会告诉你“这其实是截断后的”。你得自己比对建表语句里的 content(191) 才能确认。
更隐蔽的问题是:如果 WHERE 条件里用了函数或表达式(如 WHERE UPPER(content) = 'ABC'),哪怕 content 有前缀索引,也会直接失效——因为比较对象不再是原始前缀值。
-
key_len值突然变小,且远低于字段声明长度 × 字节因子,大概率用了前缀索引 -
Extra出现Using where; Using index说明走了覆盖索引;若只有Using where,很可能要回表,而前缀索引无法覆盖大字段本身 - 对前缀索引列做
LIKE 'xxx%'可用,但LIKE '%xxx'依然失效——和普通索引行为一致
真正卡住建索引的往往是隐式组合与排序规则
你以为只对一个大字段建索引?其实常被忽略的是:联合索引里混入了大字段,或者表用了高字节排序规则(如 utf8mb4_0900_as_cs),导致单字符估算字节数翻倍。这时即使单列没超,组合后也爆掉。
另一个坑是 innodb_large_prefix 参数。5.7 默认开启,允许单列索引前缀达 3072;但如果你的行格式是 REDUNDANT 或 COMPACT,它实际无效——必须用 DYNAMIC 或 COMPRESSED 行格式才生效。
- 检查当前设置:
SHOW VARIABLES LIKE 'innodb_large_prefix'; - 确认行格式:
SHOW CREATE TABLE t;看ROW_FORMAT=DYNAMIC是否存在 - 改行格式需重建表:
ALTER TABLE t ROW_FORMAT=DYNAMIC;,不是简单 SET 变量就能生效











