VARCHAR(255)字段不能直接建全文索引,因InnoDB单列索引长度默认上限767字节(utf8mb4下仅约191字符),超长直接报错;需指定前缀长度(如content(64))且须通过选择性测试确定合理值。

为什么 VARCHAR(255) 字段不能直接建全文索引?
MySQL 的 innodb 引擎对单列索引长度有硬限制:默认最大 767 字节(utf8mb4 下最多约 191 个字符),超长会报错 Specified key was too long。这不是“性能建议”,是直接拦停的限制。
常见错误现象:
执行 ALTER TABLE t ADD INDEX idx_content(content); 时失败,尤其当 content 是 VARCHAR(1000) 且字符集为 utf8mb4。
- 根本原因不是字段太长,而是索引页里存不下完整值 —— InnoDB 索引 B+ 树节点有固定大小(通常 16KB),前缀必须足够短才能塞进叶子节点和非叶子节点
-
utf8mb4下一个汉字占 4 字节,所以 767 ÷ 4 = 191,不是 255 - 如果启用了
innodb_large_prefix(MySQL 5.7.7+ 默认开),且行格式为DYNAMIC或COMPRESSED,上限可提到 3072 字节 —— 但不推荐无脑调高,会影响缓冲池效率
怎么算出刚好够用的前缀长度?
前缀太短,重复率高,索引失效;太长,浪费空间、拖慢写入。关键不是“最大能填多少”,而是“最小能区分多少实际数据”。
实操建议:
- 先抽样统计:用
SELECT COUNT(DISTINCT LEFT(content, N)) / COUNT(*) FROM t;测不同N下的选择性(比如 0.95 以上算合格) - 从 10 开始试,每次 +10,直到选择性不再明显上升 —— 大多数文本字段在 30~100 字符内就收敛了
- 避免用平均长度或最大长度倒推,比如
MAX(CHAR_LENGTH(content)) = 823不代表要取 823,可能前 40 个字符已覆盖 99% 差异 - 注意业务语义:日志类字段前 20 字常是时间戳+模块名,天然高区分度;用户昵称可能前 3 字就撞车,得拉到 12+ 才稳
ADD INDEX 时指定前缀长度的写法和陷阱
语法很简单,但漏掉细节就会白忙活。
正确写法:ALTER TABLE t ADD INDEX idx_content(content(64));
- 括号里的数字是字符数,不是字节数 —— MySQL 自动按字符集换算字节,
utf8mb4下就是 ×4 - 不能写成
content(255)这种“全量”幻想值,必须 ≤ 实际允许上限(767 字节对应的最大字符数) - 如果字段定义是
TEXT或MEDIUMTEXT,必须加前缀长度,否则报错BLOB/TEXT column 'content' used in key specification without a key length - 前缀索引无法用于
ORDER BY或GROUP BY—— 因为只存了开头,后面内容不可比
前缀索引查不到数据?检查这几个点
明明建了 content(64),但 WHERE content = 'xxx...' 却没走索引,大概率不是 SQL 写错,而是底层机制卡住了。
- 等值查询(
=)能用前缀索引,但仅限于“完整匹配前缀” —— 比如索引是(64),而你查的值长度 -
LIKE 'abc%'可以走,但LIKE '%abc'或LIKE '%abc%'完全不走 —— 前缀索引不支持后缀或中缀匹配 - 执行
EXPLAIN看key_len:如果是 256(utf8mb4下 64 字 × 4),说明真用了;如果为 NULL,八成是隐式类型转换或函数包裹(比如WHERE UPPER(content) = 'X') - 别指望前缀索引加速
COUNT(DISTINCT content)—— 它只加速查找,不加速去重计算
最易被忽略的一点:前缀长度一旦定下,后续改业务逻辑(比如用户昵称突然支持 emoji 表情),原来 32 字够用的前缀,现在可能因 emoji 占 4 字节而大幅降低选择性,得重新测算 —— 这事没法自动告警,得人盯。










