LIKE '%abc'或'%abc%'一定不走索引,因B+树仅支持左前缀匹配,无法定位扫描起点,只能全表扫描;FORCE INDEX无效,是索引结构硬限制。

MySQL 中 LIKE '%abc' 或 LIKE '%abc%' 一定不走 B+ 树索引,这不是配置或写法问题,是索引结构决定的硬限制——别试 FORCE INDEX,也别调统计信息,它就是不能定位起点。
为什么 LIKE '%abc' 一定全表扫描
B+ 树索引按字段值字典序存储,查找必须从一个确定前缀开始向右遍历。LIKE 'abc%' 能找到第一个 ≥ 'abc' 的记录,然后范围扫描;而 LIKE '%abc' 要匹配“结尾为 abc”的字符串,B+ 树不存反向顺序,没有起点可锚定。执行计划里 type 一定是 ALL,key 一定是 NULL。
- 复合索引(如
(status, name))救不了name LIKE '%abc'——该字段在索引中完全不可用 -
REGEXP 'abc$'同样不走索引,正则和 LIKE 一样,都不支持后缀匹配 - 隐式转换会雪上加霜:比如
name是utf8mb4_0900_as_cs,但查询字面量没声明 collation,MySQL 可能放弃索引甚至返回空结果
LIKE 'abc%' 能用索引,但有隐藏前提
不是写了 LIKE 'abc%' 就自动走索引,得看它在 WHERE 条件中的位置和索引定义是否满足最左前缀原则。
- 索引为
(a, b, c),WHERE a = 'x' AND b LIKE 'y%' AND c = 'z'→ 可用全部三列 - 索引为
(a, b, c),WHERE a = 'x' AND c LIKE 'z%'→ 实际只用到a,c被跳过(违反最左前缀) - 字段定义为
VARCHAR(255)但平均长度仅 8,建索引时用INDEX (name(10))更高效,减小体积、提升写入性能
不改 SQL 逻辑,还能怎么绕
硬扛 LIKE '%keyword%' 在数据量稍大时就会卡死。真正可行的路只有两条:换索引类型,或者换查询方式。
- 短字段(用户名、编码等):用
REVERSE(name)生成冗余列 + 普通索引,查时写成WHERE name_reversed LIKE REVERSE('abc') + '%' - 中长文本(标题、简介):建
FULLTEXT索引,改用MATCH(name) AGAINST('keyword' IN NATURAL LANGUAGE MODE)——注意ft_min_word_len默认是 4,搜 “go” 就查不到,得调配置并重建索引 - 高频模糊查且需高并发/分词/高亮:引入 Elasticsearch 或 Meilisearch,MySQL 只作原始数据源
- 覆盖索引对
LIKE '%xxx'完全无效——它连第一步“定位记录”都做不到,覆盖再多字段也没意义
最容易被忽略的一点
即使加了 FULLTEXT 索引,AGAINST 里的词若少于 ft_min_word_len(默认 4),依然查不到;字符集不一致、字段上有函数包裹、collation 隐式转换,都会让索引失效看起来像“偶然”,其实是必然。真正的瓶颈从来不在 LIKE 本身,而在你没意识到「B+ 树只认左前缀」这个边界。一旦需求突破它,就得在架构层做取舍——不是调参能解决的。


















