text字段不能直接建普通索引,须指定前缀长度(如index(content(255))),前缀按字符数计算且需兼顾区分度与性能;前缀索引仅支持left like匹配,不支持等值、排序、%xxx模糊查;推荐根据场景改用varchar、fulltext、函数索引或冗余字段。

TEXT字段不能直接建全文索引以外的普通索引
MySQL 的 TEXT、MEDIUMTEXT、LONGTEXT 类型不支持直接创建 B+ 树索引(比如 INDEX 或 UNIQUE),除非指定前缀长度。这是因为这些类型理论上可存超长内容,而 InnoDB 要求索引键长度不超过 3072 字节(默认页大小下),且单列索引不能包含未限定长度的变长大字段。
常见错误现象:ERROR 1170 (42000): BLOB/TEXT column 'content' used in key specification without a key length
- 必须显式指定前缀长度,例如
INDEX(content(255)) - 前缀长度单位是「字符数」,不是字节数(但实际占用按字符集计算:utf8mb4 下一个汉字占 4 字节)
- 如果字段常用于
WHERE content = ?或ORDER BY content,前缀索引基本无效——因为只索引开头部分,无法支撑等值或排序的完整性匹配 - 真正需要精确匹配或排序,应考虑改用
VARCHAR(16383)(InnoDB 行最大限制内)并加完整索引
前缀长度怎么选:看区分度,不是拍脑袋定255
前缀太短,大量重复前缀导致索引失效;太长,浪费空间、拖慢写入、甚至超出单索引长度上限。关键指标是「前缀区分度」:前 N 个字符能区分多少比例的行。
实操建议:
- 先采样估算:运行
SELECT COUNT(DISTINCT LEFT(content, 100)) / COUNT(*) FROM articles;,看 100 字符是否 > 0.95 - 逐步增加长度测试,直到区分度不再明显提升(比如从 0.97 到 0.972)
- 注意业务文本特征:日志类字段前 20 字高度重复,标题类可能 30 字就足够;中文比英文更需长度(因无空格分隔)
- 避免跨字符截断:utf8mb4 下,
LEFT(content, 100)可能截在某个汉字中间,但 MySQL 索引前缀是按字节还是字符?答案是字符——只要列定义为TEXT+ utf8mb4,content(100)就是取前 100 个 Unicode 字符,不会乱码
前缀索引对 LIKE 'xxx%' 有效,但对 '%xxx' 完全无效
这是最容易误用的点。前缀索引只加速“左前缀匹配”,即 WHERE content LIKE '公告:2024%' 这种,因为索引树能按字典序快速定位起始范围。
但以下情况完全用不上该索引:
-
WHERE content LIKE '%故障%'—— 全表扫描 -
WHERE content REGEXP 'error'—— 不走索引 -
WHERE content = '...'—— 即使全值相等,也只比对前缀部分,结果不可靠 -
ORDER BY content LIMIT 10—— 排序依据是完整字段,前缀索引无法支撑
如果真要支持模糊中匹配,得上 FULLTEXT 索引 + MATCH ... AGAINST,但注意它只支持自然语言模式或布尔模式,且最小词长默认 4(ft_min_word_len),短词搜不到。
替代方案比硬扛前缀索引更可靠
多数场景下,强行给 TEXT 加前缀索引是妥协方案。更合理的路径取决于具体用途:
- 只查开头固定格式(如 JSON 的
{"status":)→ 提取关键字段到新VARCHAR列,加普通索引 - 做内容关键词检索 → 改用
FULLTEXT索引,配合IN NATURAL LANGUAGE MODE,但需接受分词限制和停用词过滤 - 高频等值查询(如 hash 值、base64 编码内容)→ 存单独
CHAR(64)列,加唯一索引,应用层保证一致性 - 想加速
ORDER BY SUBSTRING(content, 1, 200)→ 创建函数索引(MySQL 8.0.13+):CREATE INDEX idx_sub ON t ((SUBSTRING(content, 1, 200)));
真正难搞的是既要模糊查中间、又要排序、还要高并发更新——这时候 TEXT 字段本身的设计就该被质疑,而不是纠结前缀该设 191 还是 255。










