like '%abc' 无法使用索引,因b+树索引仅支持前缀匹配(如'abc%'),后缀或中缀匹配无快速查找路径;倒序存储+前缀索引是高效替代方案。

LIKE '%abc' 为什么用不上索引
MySQL 对 LIKE 的索引支持非常有限:只有前缀匹配(LIKE 'abc%')能走 B+ 树索引;后缀或中缀(LIKE '%abc' 或 LIKE '%abc%')默认无法利用普通索引,因为索引是按字典序排序的,从中间或末尾找字符串没有快速路径。
常见错误现象:EXPLAIN 显示 type=ALL、key=NULL,哪怕字段上有 INDEX;查询慢到怀疑数据量,实际只是没走索引。
- 即使加了复合索引,只要
LIKE是后缀形式,该字段在索引中的位置再靠前也无效 -
COLLATION影响匹配逻辑,比如utf8mb4_0900_as_cs区分大小写,但不解决索引问题 - 全文索引(
FULLTEXT)不支持后缀匹配,只适合自然语言搜索场景
倒序存储 + 前缀索引是最快落地的替代方案
把字段值倒过来存(如 'hello' → 'olleh'),查询时也把关键词倒序('%lo' → 'ol%'),就能用上 B+ 树索引。
使用场景:用户搜「以 XX 结尾」的固定业务,比如邮箱域名('%gmail.com')、文件扩展名('%.pdf')、手机号后四位('%1234')。
- 建表时加一个生成列:
ALTER TABLE users ADD COLUMN email_rev VARCHAR(255) AS (REVERSE(email)) STORED; - 给生成列建索引:
CREATE INDEX idx_email_rev ON users(email_rev); - 查询改写:
SELECT * FROM users WHERE email_rev LIKE CONCAT(REVERSE('gmail.com'), '%'); - 注意:生成列需
STORED才能建索引;REVERSE()在 MySQL 8.0+ 支持,5.7 不行
前缀索引 + 覆盖索引能缓解但不能根治
如果必须保留原字段格式,且查询模式主要是「结尾固定长度」(比如总查最后 4 位),可考虑前缀索引配合业务层兜底。
性能影响:前缀索引本身会降低索引选择性,尤其对短字段(如 VARCHAR(10))意义不大;覆盖索引可减少回表,但无法跳过全扫描。
- 建前缀索引:
CREATE INDEX idx_phone_suffix ON users(phone(4));—— 仅对最后 4 位有效,前提是字段右对齐且长度一致 - 更稳妥的是组合条件过滤:
WHERE phone REGEXP '1234$'配合phone上的普通索引,虽然仍全表扫,但比LIKE '%1234'略快(因正则引擎有短路机制) - 避免对
TEXT字段建前缀索引超过 1000 字符,InnoDB 会报错
全文索引和 ngram 分词器不是万能解
FULLTEXT 索引只适用于自然语言匹配,对「精确后缀」无能为力;ngram 插件虽支持子串,但配置复杂、写放大高,且无法精准控制匹配位置。
兼容性影响:ngram 是 MySQL 5.7.6+ 的插件,需手动安装启用;innodb_ft_min_token_size 默认为 3,意味着单字符搜不到。
- 启用 ngram:
SET GLOBAL innodb_ft_parser = 'ngram';,然后重建索引,过程锁表 - 查「包含 abc」可用:
MATCH(content) AGAINST('abc' IN NATURAL LANGUAGE MODE),但无法表达「以 abc 结尾」 - 想模拟后缀,得靠应用层拼接:
WHERE content LIKE '%abc' AND MATCH(content) AGAINST('abc' IN BOOLEAN MODE),先用全文筛再 Like 过滤,效果有限
真正要支持高频、低延迟的后缀模糊查,倒序方案最轻量。其他方法要么改查询语义,要么引入额外组件(如 Elasticsearch),别在 MySQL 里硬扛。











