在MySQL业务中,LIKE模糊查询是最常用的检索手段之一,但写法不同会导致性能差距极大。很多接口在用户量上涨后突然出现慢查询,排查发现都是因为模糊匹配无法命中索引。本文从索引原理、改写技巧、替代方案三个层面,详细讲解如何让LIKE查询跑得更快。

一、LIKE查询为什么慢
MySQL的默认存储引擎InnoDB使用B+树作为索引结构,索引键值从左到右有序排列。当执行LIKE 'abc%'时,优化器可以从索引树的根节点定位到以abc开头的第一条记录,再顺着叶子节点向右扫描,这个过程能够利用索引范围查找,效率很高。
但是一旦通配符出现在开头,例如LIKE '%abc'或LIKE '%abc%',由于不知道前缀是什么,数据库无法在有序的索引树上做定位,只能从第一条记录开始逐行比对,也就是全表扫描。在数据量达到百万级以后,这种扫描会消耗大量IO和CPU,响应时间呈线性增长。
二、前缀匹配的索引利用
如果业务允许,尽量把模糊查询改成前缀匹配。例如根据手机号后四位搜索,可以存储一个反转后的手机号字段,并对该字段建索引,查询时写成前缀匹配。
-- 原表增加反转手机号列 ALTER TABLE user ADD COLUMN phone_reverse VARCHAR(20); UPDATE user SET phone_reverse = REVERSE(phone); CREATE INDEX idx_phone_rev ON user(phone_reverse); -- 查询手机号后四位为1234的用户 SELECT * FROM user WHERE phone_reverse LIKE '4321%';
上面这种方式把后缀匹配转换成了前缀匹配,能够正常走索引。需要注意的是,REVERSE函数应在写入时计算并冗余存储,而不要在WHERE子句中对原字段使用REVERSE,否则函数包裹列同样会导致索引失效。
对于普通的前缀搜索场景,直接对查询列建立二级索引即可。如果查询只返回索引包含的列,还可以建成覆盖索引,避免回表。例如SELECT id,name FROM user WHERE name LIKE '张%',对(name)建索引就能在索引树内完成查找与取值。
三、使用全文索引处理中段匹配
当必须使用“%关键词%”这种包含中段匹配的需求,比如文章内容搜索,B+树索引无能为力。MySQL从5.6版本开始为InnoDB提供全文索引(FULLTEXT),它基于倒排索引实现,适合自然语言检索。
-- 对文章标题和正文建立全文索引
ALTER TABLE article ADD FULLTEXT INDEX ft_title_body (title, body);
-- 使用MATCH AGAINST代替LIKE
SELECT * FROM article
WHERE MATCH(title, body) AGAINST('数据库优化' IN NATURAL LANGUAGE MODE);
全文索引把文本切分成词项,查询时直接定位包含词项的文档,性能远好于LIKE全表扫。不过需要注意,MySQL内置分词器对中文支持较弱,通常需要借助ngram插件(如ngram_token_size=2)来实现中文二元分词,否则可能匹配不准。
此外,全文索引有一定局限性:它不支持前缀通配符,排序相关性计算也会消耗资源。对于简单的短文本中段匹配,可以接受;但如果是复杂的电商商品搜索,建议把数据同步到专门搜索引擎。
四、借助外部组件承接搜索
当模糊查询成为系统瓶颈且场景复杂时,应当从架构层面解耦。常见的做法是把需要搜索的数据写入Elasticsearch或Redis,由它们提供模糊检索能力。
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| LIKE+索引改写 | 简单前缀匹配 | 零额外组件,改造成本低 | 无法支持任意位置模糊 |
| FULLTEXT | 中等规模文本搜索 | 数据库内置,事务一致 | 中文分词弱,功能有限 |
| Elasticsearch | 海量复杂搜索 | 近实时,分词强大 | 需维护同步与集群 |
以Elasticsearch为例,可以通过Logstash或业务双写将MySQL数据同步过去,前端搜索请求直接打向ES,MySQL只负责持久化。这样即使LIKE无法优化,也不会影响主库性能。
如果仅是少量固定后缀或枚举值匹配,也可以用Redis的Keyspace或者有序集合做缓存映射,但需注意Redis的KEYS命令生产环境禁用,应使用SCAN替代。
五、执行计划验证不可或缺
任何优化都要用EXPLAIN验证。在查询前加上EXPLAIN,观察type列与key列,确认是否走了预期索引,以及rows估算是否合理。
EXPLAIN SELECT * FROM user WHERE name LIKE '张%'; -- 若key显示idx_name且type为range,说明索引生效
有时即使写了前缀LIKE,如果字段字符集或排序规则不一致、或者查询条件混合了其他函数,也会导致索引失效。因此上线前务必在真实数据量下跑一遍执行计划,并结合慢查询日志持续监控。
总结来说,优化MySQL的LIKE模糊查询,核心思路是让通配符不出现在开头、用冗余列或反转策略转化匹配方向、在中段匹配时引入全文索引或外部搜索引擎,最后用EXPLAIN兜底验证。根据业务体量选择合适的组合,才能兼顾开发成本与查询性能。