你知道吗?`LIKE '%abc'`这种后缀匹配之所以慢,是因为B+树索引按字典序从左到右组织,根本没法反向定位后缀。所以即使有索引,也照样触发全表扫描。只有`LIKE 'abc%'`这类前缀匹配才能走索引,而且还得满足索引存在、选择性高、没有函数干扰这些前提条件。

直接说结论:LIKE可不是万能的模糊搜索工具。尤其是LIKE '%关键词%'这种写法,在百万级以上的数据表里基本等于拒绝响应——不是慢的问题,是压根没得优化。
LIKE '%abc' 后缀匹配为什么一定慢?
核心原因就一个:B+Tree索引按字符串字典序从左到右组织,数据库没法从末尾反向跳查。哪怕你给name字段加了INDEX(name),执行WHERE name LIKE '%abc'依然逃不掉全表扫描。
那有没有补救办法?有,但要分版本处理:
- MySQL 8.0+:可以直接建函数索引——
CREATE INDEX idx_name_rev ON t((REVERSE(name))),查询改写为WHERE REVERSE(name) LIKE REVERSE('abc') || '%'。 - 低版本MySQL或PostgreSQL:老老实实加个反向列——
ALTER TABLE t ADD COLUMN name_rev VARCHAR(255) AS (REVERSE(name)) STORED,再对name_rev建普通索引。 - 避坑提醒:别用
SUBSTR(name, -3) = 'abc'——函数一旦作用在字段上,所有索引立刻失效。
LIKE 'abc%' 前缀匹配真能走索引?
答案是:能,但有前提。首先索引必须存在,其次优化器得相信这个条件有足够的选择性。比如name LIKE 'A%'要是命中了80%的行,优化器大概率会弃索引选全表扫描——它不傻,全表比索引还快的时候何必自找麻烦。
怎么确认索引是否生效?用EXPLAIN看执行计划:type为range、key显示索引名、rows远小于总行数才算有效。另外,前缀越长选择性越好——LIKE 'ZhangLiu%'比LIKE 'Z%'更可能走索引。还有一点要当心:存储过程里别拼成CONCAT('%', @kw)或REPLACE(@kw, '_', '_') ESCAPE ''——这类动态拼接常让优化器放弃索引。
中文和特殊字符怎么不出错?
同一个LIKE '张%',在utf8mb4_general_ci和utf8mb4_0900_as_cs下可能返回完全不同的结果;搜字符串'10%'如果不加转义,就会变成“匹配任意长度数字后跟%”。这些小细节一旦忽略,排查起来相当头疼。
几个实用建议:
- 先查字段排序规则:
SHOW FULL COLUMNS FROM users LIKE 'name',确认Collation是否支持你要的匹配语义。 - 搜索实际含
%或_的内容时,必须用ESCAPE:WHERE comment LIKE '%10!%' ESCAPE '!',别只写'%10%%'。 - 繁体/简体统一匹配?MySQL推荐
utf8mb4_unicode_ci;PostgreSQL用ILIKE配合unaccent()函数。 - 注意:
LIKE不匹配NULL,要查空值得额外加OR column IS NULL。
真正卡住人的从来不是语法写不对,而是把LIKE当全文检索使——它只做字符序列比对,不理解分词、同义、拼音或语义。该上MATCH AGAINST、tsvector或Elasticsearch的地方,硬扛只会拖垮整个库。
