LIKE '%xxx'或'%xxx%'必全表扫描,因B+树索引仅支持最左前缀匹配,%开头无法定位扫描起点,优化器只能逐行检查,执行计划显示type=ALL、key=NULL。

LIKE '%xxx' 或 LIKE '%xxx%' 一定会触发全表扫描,不是你写错了,是 B+ 树索引根本没法支持这种匹配。
为什么 LIKE '%xxx' 必须全表扫描
数据库的 B+ 树索引按字段值排序存储,只支持从左到右的“最左前缀”定位。LIKE '%abc' 的 % 在开头,等于告诉优化器:“我不知道第一个字符是什么”,索引就失去了定位能力。哪怕 name 字段上有 idx_name 索引,执行计划里也会显示 type: ALL、key: NULL —— 这不是 bug,是设计如此。
- MySQL、PostgreSQL、SQL Server 行为一致,Oracle 同样不例外
-
LIKE 'abc%'能走range扫描,因为可以定位到所有以'abc'开头的索引页 -
LIKE 'ab%c'中间有 %,同样无法用索引加速,优化器会直接跳过索引 - 加
FORCE INDEX也没用:优化器知道索引无效,强制也查不到数据
能走索引的 LIKE 写法只有这几种
别被“模糊搜索”这个词带偏——真正能进索引的,只有严格满足前缀约束的模式。
-
LIKE 'admin%':可走索引查找(Index Seek),快 -
LIKE 'a[bc]d%':方括号表达式在 SQL Server 中仍属前缀范围,只要开头固定,仍可能走索引 -
LIKE N'张%':注意加N前缀,避免隐式转换导致索引失效(尤其 Unicode 字段) -
UPPER(col) LIKE 'ABC%':函数包裹会让索引失效,除非你建了函数索引(如 MySQL 8.0+ 的INDEX (UPPER(col)))
必须支持中间或后缀匹配?试试这三种替代方案
业务真要查 “包含 xxx” 或 “以 xxx 结尾”,硬扛 LIKE '%xxx%' 是自找死路。优先级从高到低:
- 改业务逻辑:把“搜商品名含关键词”变成“输入前缀自动补全”,前端限制用户只能输开头几个字
- 建反向索引:
ALTER TABLE users ADD INDEX idx_name_rev (REVERSE(name));,然后查REVERSE(name) LIKE REVERSE('%abc')→ 实际变成REVERSE(name) LIKE 'cba%',可走索引 - 上全文索引:
ALTER TABLE articles ADD FULLTEXT(title, content);,再用MATCH(title) AGAINST('xxx' IN NATURAL LANGUAGE MODE);注意中文需分词插件,且ft_min_word_len默认是 4,短词搜不到
最容易被忽略的三个坑
很多慢查询修复不了,不是没建索引,而是掉进了这些细节陷阱:
- 字段是
VARCHAR但用了COLLATE Latin1_General_CS_AS(区分大小写),而用户输的是小写 ——LIKE 'Admin%'就匹配不到admin123,你以为没走索引,其实是没数据 - 字段类型是
TEXT或MEDIUMTEXT,MySQL 不允许直接建普通索引,得先加前缀长度:INDEX(name(255)) - WHERE 里混用了
OR:name LIKE 'a%' OR name LIKE '%b',整个条件都会让索引失效,哪怕左边能走索引
真正卡住性能的,从来不是数据量,而是你写的那行 LIKE 有没有让优化器“看懂”你的意图。索引不是开关,是路径;% 不是通配符,是放弃索引的信号。写之前,先问自己:这个模式,B+ 树能不能从左边开始“猜”出第一个字?猜不出,就别指望它快。

















