text字段建索引必须指定前缀长度,如content(255),因mysql禁止对text类字段建无长度限制的b+树索引;选长需基于数据分布测试区分度,且仅优化left(content,n)、like 'xxx%'等左前缀查询。

TEXT字段建索引必须指定前缀长度,否则报错
MySQL 不允许对 TEXT、MEDIUMTEXT、LONGTEXT 字段直接建普通 B+ 树索引。执行 CREATE INDEX idx ON t(content) 会立刻失败,错误信息是:ERROR 1170 (42000): BLOB/TEXT column 'content' used in key specification without a key length。这不是配置问题,是 InnoDB 强制要求:必须显式写明字符数,例如 content(255)。
注意:255 是「字符数」,不是字节数。UTF8MB4 编码下,一个汉字占 4 字节,所以 content(255) 实际占用索引空间最多达 1020 字节——得确认是否超出 innodb_large_prefix 开启后的 3072 字节单索引上限(否则会报 ERROR 1071)。
怎么选前缀长度?别猜,用 SELECT DISTINCT LEFT() 测
前缀太短,大量值开头相同,索引区分度低,优化器可能直接弃用;太长,又失去“节省空间”的意义。真实做法是查数据分布:
-
SELECT COUNT(DISTINCT LEFT(content, 60)) / COUNT(*) AS sel60 FROM article;—— 看前 60 字符能否覆盖 92% 以上记录 - 再试
80、100,直到sel_x ≥ 0.92且增幅明显放缓 - 如果
sel60 = 0.3,sel100 = 0.85,sel120 = 0.93,那就选120
别只看平均长度。比如日志内容多以 [ERROR] 开头,前 10 字几乎全是重复的,这时再加长也没用——得换思路,比如抽离 level 字段单独建索引。
WHERE 条件必须匹配左前缀,否则索引白建
前缀索引只加速 LIKE 'xxx%' 和 = 'xxx...' 这类从开头严格匹配的查询。以下全部无效:
-
WHERE content LIKE '%error%'→ 全表扫描,EXPLAIN显示type: ALL -
WHERE content LIKE '%.log'→ 后缀模糊,需用反向列 +REVERSE()改写 -
ORDER BY content或GROUP BY content→ 索引不含完整值,无法排序分组 -
SELECT content FROM ... WHERE ...→ 即使走了索引,仍要回表读完整 TEXT,IO 开销大
Java 中拼参数时尤其小心:ps.setString(1, keyword + "%") ✅;ps.setString(1, "%" + keyword + "%") ❌。
比前缀索引更常用的有效方案其实是绕开它
很多团队卡在“一定要给 TEXT 加索引”这个思维定式里。其实业务上真正需要的往往不是“前缀匹配”,而是:
- 全文关键词搜索 → 改用
FULLTEXT索引 +MATCH(content) AGAINST(? IN NATURAL LANGUAGE MODE),中文记得配ngram解析器 - 查某段摘要是否存在 → 插入时用 Java 提取前 120 字存为
snippet VARCHAR(255),再建普通索引 - 判断内容是否重复 → 建虚拟列
content_hash CHAR(32) AS (MD5(content)) STORED,索引该哈希值 - 后缀匹配(如
file_path LIKE '%.pdf')→ 加path_rev VARCHAR(255) AS (REVERSE(file_path)) STORED,再建索引
前缀索引不是默认选项,它是特定数据特征(前缀高区分度 + 查询模式固定左匹配)下的窄带解法。多数 TEXT 模糊查询场景,它连入场券都拿不到。











