b+树索引无法跳过前缀定位,因其排序严格遵循“从左到右”的字典序,跳过最左列则失去查找起点,导致无法利用有序性进行二分查找,只能全表扫描。

为什么B+树索引无法跳过前缀定位
B+树索引的排序是严格“从左到右”的字典序,比如 name 字段索引值实际存储为:"apple"、"banana"、"cat"、"dog"……这种结构天然支持快速定位“以 a 开头”的范围,但完全不记录“以 g 结尾”或“中间含 at”的位置信息。
当执行 WHERE name LIKE '%g' 时,MySQL 必须确认哪些节点可能包含结尾为 g 的字符串——而这些字符串在 B+ 树中是离散分布的("dog" 在叶子页 A,"egg" 在叶子页 C,"pig" 可能在页 F),索引无法给出任何起始查找点,只能放弃索引,走全表扫描。
EXPLAIN 看得见的失效信号
执行 EXPLAIN SELECT * FROM goods WHERE name LIKE '%g'; 后,重点关注三列:
-
type显示为ALL(而非range或ref) -
key为NULL(表示没用上任何索引) -
rows接近表总行数(说明预估要读全表)
这几个信号同时出现,基本可以断定索引已失效。注意:即使 possible_keys 列显示有索引名,只要 key 是 NULL,就代表这次查询根本没用上它。
不是所有 LIKE 都失效:前缀匹配仍有效
索引是否失效,只跟通配符位置有关,和字段类型、字符集、数据量无关:
-
LIKE 'abc%'→ 有效:B+ 树能定位到第一个 ≥'abc'的记录,然后向右顺序遍历 -
LIKE '%abc'→ 失效:无起点,必须逐行检查 -
LIKE '%abc%'→ 失效:仍是无起点的任意位置匹配 -
LIKE 'a_c'(下划线)→ 有效:长度固定 + 前缀确定,仍可利用索引缩小范围
特别提醒:LIKE 'abc%' 即使后面跟着中文或 emoji(utf8mb4),只要排序规则兼容,依然走索引;但 LIKE '%中文' 无论字符集多规范,照样全表扫。
反向存储是绕过限制的务实解法
如果业务强依赖后缀搜索(比如查邮箱域名 WHERE email LIKE '%@gmail.com'),又不能上全文索引或 ES,可以手动构造“反向索引”:
新增一个 email_reversed 字段,写入 REVERSE(email),再给它建普通 B+ 树索引。查询时把条件也反转:WHERE email_reversed LIKE 'moc.liamg@%' —— 这就又变成前缀匹配了。
这个方案代价小、兼容性好,但要注意两点:
- 应用层必须保证正反字段同步更新(推荐用触发器或 ORM 层封装)
- 不能用于模糊中间段(如
LIKE '%keyword%'),因为反转后仍是中间匹配,依然无效
真正难处理的是 LIKE '%xxx%' 场景——它既不能靠反向存储解决,也不适合用覆盖索引硬扛,这时候该考虑是否真的需要数据库做模糊检索,还是把压力交给更合适的工具,比如 FULLTEXT 或外部搜索引擎。











