LIKE '%abc'一定走全表扫描,因B+树索引仅支持从左到右最左前缀匹配,无法倒序定位以'abc'结尾的记录,只能逐行读取并字符串匹配。

LIKE '%abc' 为什么一定走全表扫描
B+ 树索引是按字典序严格排序的,叶子节点里存的是 key → row_id 映射。当查询写成 LIKE '%abc',MySQL 无法从任意位置“倒着”定位前缀,只能逐行读取所有记录、提取字段值、再做字符串匹配——这本质上就是全表扫描。
即使你给 name 字段建了普通索引,EXPLAIN 的 type 也会是 ALL,key 是 NULL。
- 只有
LIKE 'abc%'这种左前缀模式才能用上索引(前提是该列是独立索引,或联合索引最左列) -
LIKE '%abc%'同样失效,B+ 树不支持子串中段定位 - 注意:大小写敏感性(如
utf8mb4_0900_as_cs排序规则)不会影响是否走索引,只影响匹配结果
不是所有模糊需求都得硬扛索引
如果你真需要查“包含某串”或“以某串结尾”,别在普通索引上死磕。MySQL 原生支持的替代方案有限,但有明确分层:
- 查“结尾”:可建反向索引,比如加一列
reverse_name VARCHAR(64),写入时存REVERSE(name),查询改用reverse_name LIKE 'cba%' - 查“任意位置”:优先考虑
FULLTEXT索引(仅限MyISAM或InnoDB的 CHAR/VARCHAR/TEXT 列),配合MATCH() AGAINST() - 高并发、多条件、模糊+过滤+分页:外部搜索引擎(如 Elasticsearch)才是合理解,MySQL 不是为这个设计的
WHERE 中混用 LIKE 和其他条件时的坑
很多人以为 “加了索引 + 用了 LIKE 'abc%' 就万事大吉”,其实联合索引下很容易掉进陷阱:
- 索引是
(status, name),但查询写成WHERE name LIKE 'abc%' AND status = 1—— 因为没按最左前缀顺序写,name列实际不生效 - 写成
WHERE status = 1 AND name LIKE 'abc%'才能命中(status, name)的前缀路径 - 如果后面还跟了
ORDER BY created_at,而created_at不在索引里,就会触发 filesort;若在索引里但位置靠后(比如(status, name, created_at)),又可能因范围查询导致created_at无法用于排序
容易被忽略的细节:ESCAPE 和字符集
LIKE 语句里如果显式用了 ESCAPE,但转义字符本身出现在通配符位置,会导致优化器误判执行计划:
- 例如
name LIKE '\%abc' ESCAPE '\',本意是查字面量%abc,但如果\在某些字符集下被当作多字节字符开头,可能解析失败 - 更隐蔽的是 JOIN 场景:关联字段字符集不一致(如一边是
utf8mb4,一边是utf8),即使LIKE 'abc%'写对了,也可能触发隐式转换,让索引失效 - 验证方法始终是
EXPLAIN,重点盯key是否非空、type是否降到range或更好
LIKE 的语义和 B+ 树的物理结构之间存在不可调和的矛盾。一旦模糊逻辑绕不开左通配,就该果断换技术栈,而不是在 SQL 里反复调参。


















