LIKE '%abc'无法使用索引(包括FORCE INDEX)是因为B+树结构不支持后缀匹配,无法定位扫描起点,只能全表扫描;只有'abc%'等左前缀模式才能利用索引范围查询。

LIKE '%abc' 为什么连 FORCE INDEX 都没用
因为这不是优化器“选不选”的问题,而是 B+ 树压根没法支持这种查找方式——FORCE INDEX 强制指定索引后,MySQL 仍会检查该索引是否能提供有效扫描起点;发现 WHERE name LIKE '%abc' 无法确定第一个匹配记录在索引中的位置,就直接放弃,退回到 type: ALL。这不是 bug,是结构限制。
B+ 树只认“从左开始的连续前缀”
B+ 树叶子节点按字段值字典序严格排序,查找必须能“跳到第一个可能命中项”。LIKE 'abc%' 可以快速定位到 ≥ 'abc' 的第一条记录,然后向右遍历;但 LIKE '%abc' 要求结尾为 'abc',而索引里没有按字符串结尾排序的副本,所有候选值(如 'xabc'、'123abc'、'abc')散落在不同层级和分支中,无法锚定起点。
常见错误现象包括:
- 明明
name字段有idx_name索引,EXPLAIN却显示key: NULL、type: ALL - 把
WHERE name LIKE '%张%'改成WHERE name LIKE '张%'后,执行时间从 2.3s 降到 42ms - 加了
ORDER BY name或LIMIT 10也无济于事——没起点,限多少都得先扫全表
哪些 LIKE 写法其实能走索引
能否命中索引,只看通配符是否出现在模式最左侧。只要开头是确定字符,哪怕中间或结尾有通配符,B+ 树仍可利用前缀定位:
-
WHERE col LIKE 'abc%'→ ✅ 走range扫描,等价于col >= 'abc' AND col < 'abd' -
WHERE col LIKE 'ab_c'→ ✅ 下划线_是单字符通配,不破坏前缀连续性,仍可定位到'ab'范围 -
WHERE col LIKE 'ab%c'→ ⚠️ 仅前缀'ab'部分生效,后续仍需逐行做字符串匹配,type可能是range,但实际过滤效率低 -
WHERE col LIKE '%abc'或'%abc%'→ ❌ 绝对不走索引,key恒为NULL
别在原生 LIKE 上硬刚后缀/中间匹配
想支持 '%abc' 这类查询,不是调优 SQL 就能解决的。真实业务中可行路径只有三条:
- 加反向字段:比如原字段
name,额外维护name_reversed(存REVERSE(name)),查后缀就转成前缀查:WHERE name_reversed LIKE 'cba%' - 用全文索引:对长文本启用
FULLTEXT,配合MATCH ... AGAINST,但不适用于短字段或精确后缀场景 - 引入外部引擎:Elasticsearch 或 ClickHouse 做模糊检索,MySQL 只负责主键精准查询和事务
最容易被忽略的一点是:哪怕你建了联合索引,比如 (status, name),只要 name 上的 LIKE 是 '%abc',整个索引对 name 部分就形同虚设——status = 1 可能缩小范围,但 name 条件仍触发全索引扫描或回表,性能瓶颈照旧。


















