LIKE '%keyword' 用不上索引是因为B+树仅支持前缀匹配,'%keyword'属后缀或通配符居中模式,无法利用有序结构查找,导致全表扫描。

LIKE '%keyword' 为什么用不上索引
MySQL 对 LIKE 的索引支持非常苛刻:只有当模式以固定前缀开头(如 'abc%')时,B+ 树索引才能生效;一旦写成 '%abc' 或 '%abc%',就只能全表扫描。这不是 MySQL 偷懒,而是 B+ 树本身不支持“后缀匹配”——它按字典序组织数据,没法从结尾倒着查。
常见错误现象:EXPLAIN 显示 type=ALL、key=NULL,哪怕字段加了普通索引也毫无作用。
- 使用场景:搜索框支持任意位置匹配(如用户搜“苹果”,想命中“红富士苹果”“青苹果汁”)
- 参数差异:
LIKE 'apple%'走索引,LIKE '%apple'不走,LIKE '%apple%'同样不走 - 性能影响:10 万行表上
LIKE '%xx%'可能比加索引前还慢——因为额外多了索引树遍历开销
前缀索引不是万能解药,得看字段分布
前缀索引(如 INDEX (title(10)))只对 LIKE 'xxx%' 有效,且效果高度依赖字段值的区分度。比如对邮箱字段建 email(5) 索引,可能前 5 个字符全是 user@,索引几乎失效。
实操建议:
- 先用
SELECT COUNT(DISTINCT LEFT(column_name, N)) / COUNT(*) FROM table_name;算区分度,>0.9 才值得建 - 别盲目设长:
title(20)比title(100)更可能被缓存进内存,查询更快 - 注意字符集:UTF8MB4 下一个汉字占 4 字节,
name(10)实际可能只截 2–3 个汉字 - 建完立刻验证:
EXPLAIN SELECT ... WHERE column LIKE 'abc%';确认key列有值
真正要支持 '%keyword%',绕不开全文索引或外部方案
MySQL 原生支持的解决方案只有 FULLTEXT 索引,但它要求引擎是 MyISAM 或 InnoDB(5.6+),且默认只对长度 ≥4 的词分词,停用词(如“的”“和”)会被忽略。
实操要点:
- 建索引:
ALTER TABLE articles ADD FULLTEXT(title, content); - 查法必须用
MATCH() AGAINST(),不能混用LIKE:SELECT * FROM articles WHERE MATCH(title) AGAINST('数据库优化' IN NATURAL LANGUAGE MODE); - 模糊程度有限:不支持通配符(
AGAINST('db*')在布尔模式下才支持,但需配置ft_min_word_len) - 性能陷阱:全文索引更新成本高,高频写入表慎用;短文本(如用户名)不适合,分词效果差
生产环境更常见的折中路径
纯靠 MySQL 解决任意位置模糊查,基本等于在限制条件下找平衡点。多数团队实际落地时会组合几种策略:
- 前端加约束:搜索框提示“请输入关键词开头”,强制用户输入
app而非ple,后端转成WHERE name LIKE 'app%' - 冗余字段预处理:对需要模糊查的字段,额外生成 n-gram 片段存到新列(如 “苹果” → “苹”、“苹果”、“果”),对该列建普通索引 +
=查询 - 异步导出到 Elasticsearch:当数据量 >100 万或需高亮/同义词/拼音搜索时,MySQL 就该让位了
- 注意 collation:如果字段是
utf8mb4_unicode_ci,大小写和重音不敏感,但utf8mb4_bin下'Apple' != 'apple',建索引前得确认业务是否接受
最常被忽略的是字符集与排序规则对索引行为的隐式影响——同一个 SQL,在 _ci 和 _bin 校对集下,可能一个走索引、一个不走。











