前缀索引仅加速where column like 'xxx%'类前缀匹配查询,无法优化select content或like '%xxx%'操作;其核心价值在于减少索引空间并提升前缀过滤效率,但真正性能瓶颈常源于大字段引发的io与内存开销。

前缀索引不能加速 SELECT content 或 WHERE content LIKE '%xxx%' 这类操作——它只对 WHERE content LIKE 'xxx%' 有效,且必须配合合理的前缀长度;真正拖慢查询的,往往不是索引缺失,而是大字段本身带来的 IO 和内存开销。
为什么加了 content(50) 索引,EXPLAIN 显示 key_len 很小但查询还是慢
因为前缀索引只存前 N 字节,MySQL 用它只能做“前缀匹配”,无法支撑全文扫描或模糊中间匹配。更关键的是:SELECT * 或 SELECT content 会强制把整段 BLOB/TEXT 从磁盘读入 Buffer Pool、再序列化传给客户端,哪怕你只查 1 行,也可能触发几 MB 的随机 IO。
- 检查执行计划时重点看
Extra字段:如果出现Using filesort或没Using index,说明索引没被用于覆盖查询 -
key_len小(比如只有 53)意味着实际只用了前缀部分,但不代表这个前缀能过滤掉数据——得结合rows和filtered判断真实效率 - UTF8MB4 下,
content(50)实际最多存 12 个汉字(每个占 4 字节),别按“字符数”设长度,要用LENGTH()而非CHAR_LENGTH()统计
怎么选对前缀长度:别猜,用 SELECT 算出来
目标不是“尽可能长”,而是让前缀具备足够区分度——即在最小长度下,90%+ 的值能被唯一识别。拍脑袋设 content(100) 可能浪费空间,设 content(20) 又几乎不生效。
- 先看数据分布:
SELECT LEFT(content, 200) AS p, COUNT(*) FROM articles GROUP BY p ORDER BY COUNT(*) DESC LIMIT 10;找出高频重复前缀 - 再算选择性:
SELECT COUNT(DISTINCT LEFT(content, 80)) / COUNT(*) AS sel_80 FROM articles;对比sel_50、sel_100,找提升率明显放缓的拐点 - 最终建索引时,长度建议比“拐点长度”多留 10~20 字节缓冲,比如
sel_80=0.92、sel_100=0.94,那就用content(120)
LIKE '%xxx' 和 LIKE '%xxx%' 根本没法用前缀索引
只要 LIKE 模式以 % 开头,B-tree 索引(包括前缀索引)就完全失效,MySQL 只能全表扫描。这时候加索引不仅没用,还会拖慢写入、占用额外磁盘空间。
- 如果是后台管理搜索,优先把关键信息抽成独立字段:比如从
content提取前 60 字存为snippet,加INDEX(snippet) - 真要支持任意位置关键词检索,改用
FULLTEXT索引 +MATCH() AGAINST(),但注意它默认忽略小于 4 字符的词,且停用词列表不可控 - 高并发或大数据量场景下,同步内容到 Elasticsearch 更现实——MySQL 不是搜索引擎,硬扛会拖垮整个实例
拆分字段比优化索引更能立竿见影
把一个 LONGTEXT 字段拆成 title(VARCHAR(255))、summary(VARCHAR(500))、body_hash(CHAR(32))三个字段,查询性能和可维护性会立刻改善。
-
title和summary加普通索引,支撑WHERE title LIKE 'xxx%'或ORDER BY summary -
body_hash存 MD5 或 SHA256,用于快速判重或变更检测,避免每次读完整内容比对 - 真正需要读大文本时,用
SELECT body FROM ... WHERE id = ?单独查,避免和高频查询混在一起污染 Buffer Pool
最常被忽略的一点:优化长文本字段,本质不是“怎么索引更快”,而是“怎么尽量不读它”。索引只是辅助,字段设计才是根因。一旦业务允许,优先拆、其次哈希、最后才考虑前缀长度微调。











