MySQL LIKE 模糊查询为什么慢?怎么优化?
简化版
LIKE 'abc%' 可以利用普通 B+ 树索引做前缀范围扫描,LIKE '%abc' 或 LIKE '%abc%' 通常无法利用普通索引定位,只能扫描更多数据;优化可用前缀匹配、反向字段、全文索引或搜索引擎。
详细版
B+ 树索引按从左到右的字符串顺序组织,因此前缀已知时可以定位范围。比如 name LIKE '张%' 可以扫描以“张”开头的一段索引。
但 name LIKE '%三%' 的开头未知,数据库无法从索引树上定位起点,只能检查大量候选行。数据量小时问题不大,千万级表上就可能变成慢查询。
优化时要先明确业务搜索语义:如果只需要前缀搜索,改成 abc% 并建索引;如果需要后缀搜索,可存反向字段;如果需要全文检索、分词、相关性排序,应该考虑 MySQL FULLTEXT 或 Elasticsearch 等搜索方案。
完整版教学
一、LIKE 慢不慢取决于模式
LIKE 本身不是一定慢,关键看通配符位置。
-- 可能用索引
SELECT * FROM users WHERE name LIKE 'Tom%';
-- 普通 B+ 树索引难以定位
SELECT * FROM users WHERE name LIKE '%Tom%';
Tom% 表示前缀确定,数据库可以在索引中找到以 Tom 开头的范围。%Tom% 表示前面可以有任意字符,索引无法知道从哪里开始找。
记忆钩子:B+ 树擅长“从左往右找前缀”,不擅长“中间任意包含”。
二、为什么前缀匹配能用索引
字符串索引按字典序排列。
Tom
Tommy
Tony
Trace
查询 LIKE 'Tom%' 时,匹配值集中在一段连续范围中:
CREATE INDEX idx_name ON users(name);
SELECT id, name
FROM users
WHERE name LIKE 'Tom%';
执行近似为:
定位 >= 'Tom' 的位置
-> 向后扫描直到不再以 Tom 开头
如果以 Tom 开头的只有 500 行,而全表 1000 万行,索引收益很明显。
三、为什么左模糊很难用普通索引
LIKE '%Tom' 或 LIKE '%Tom%' 开头未知,匹配值在索引中分散。
ATom
HelloTom
MyTomCat
Tom
这些值在字典序中不一定连续,B+ 树无法通过一次范围定位找到它们。数据库可能只能扫描索引或扫描表,再逐行判断字符串是否匹配。
这类查询在小表上能跑,在大表上会拖垮性能。面试中要说清楚:不是索引失效这个词本身,而是无法利用索引有序性定位范围。
四、后缀搜索可以用反向字段
如果业务是查“以某个后缀结尾”,可以额外存一个反向字段。
-- 原字段
email = 'abc@example.com'
-- 反向字段
reverse_email = 'moc.elpmaxe@cba'
查询以 example.com 结尾的邮箱,可以转成前缀匹配:
SELECT *
FROM users
WHERE reverse_email LIKE REVERSE('example.com') || '%';
MySQL 字符串拼接语法按版本和模式可能不同,实际可在应用层先计算反向字符串。核心思路是把后缀匹配转换成前缀匹配。
五、包含搜索要考虑全文检索
如果需求是文章内容、商品标题、评论关键字搜索,普通 LIKE '%keyword%' 往往不是长期方案。
可选方案:
| 方案 | 适合场景 | 代价 |
|---|---|---|
| 前缀 LIKE | 简单前缀搜索 | 不能做任意包含 |
| FULLTEXT | MySQL 内置全文检索 | 分词和排序能力有限 |
| Elasticsearch | 搜索体验复杂 | 需要额外系统和同步 |
| 倒排索引服务 | 高级搜索 | 架构成本更高 |
如果只是后台低频模糊查,LIKE 可以接受;如果是用户高频搜索入口,应按搜索系统设计。
六、还要减少扫描范围
即使必须使用 %keyword%,也可以通过其他条件减少候选集。
SELECT id, title
FROM articles
WHERE status = 'published'
AND created_at >= '2026-01-01'
AND title LIKE '%MySQL%'
LIMIT 20;
如果 status + created_at 能先把候选从 1000 万行降到 5 万行,再做 LIKE 判断,成本会低很多。
索引可以考虑:
CREATE INDEX idx_status_time ON articles(status, created_at);
这不是让 %MySQL% 本身用上索引,而是让其他条件先筛掉大部分数据。
七、常见误区与追问
- 误区:所有 LIKE 都不能走索引。 前缀匹配如
abc%可以利用 B+ 树索引。 - 误区:
%abc%建普通索引就能快。 普通索引无法定位任意包含位置,通常仍要扫描大量数据。 - 误区:全文检索可以无成本替代 LIKE。 FULLTEXT 或搜索引擎需要分词、同步、排序和一致性设计。
- 误区:低频后台查询也必须上搜索引擎。 低频、小数据量、可接受延迟时 LIKE 可以保留。
- 追问:后缀匹配怎么优化? 可以存反向字段,把后缀搜索改成反向字段的前缀搜索。
- 追问:必须包含搜索怎么办? 用其他条件缩小范围,或引入全文索引/搜索引擎。
八、加强记忆
LIKE 优化记住通配符位置:右边 % 还能用前缀范围,左边 % 基本丢掉普通索引定位能力。前缀搜索靠 B+ 树,后缀搜索靠反向字段,全文搜索靠倒排索引或专门搜索系统。