LIKE '%abc'一定不走索引,因为B+树仅按字段完整字典序组织,无法支持后缀匹配,优化器判定全表扫描成本更低;EXPLAIN中key=NULL、type=ALL即确认索引被弃用,FORCE INDEX等无效。

LIKE '%abc' 为什么一定不走索引
因为 B+ 树索引只按字段值的完整字典序组织,不存反向顺序、也不存后缀排序。查询 LIKE '%abc' 要找“以 abc 结尾”的记录,但索引无法告诉你从哪一页开始查——没有确定前缀,树就无法导航。优化器算过成本,发现全表扫描(type: ALL)比随机跳转还快,索性放弃索引。
EXPLAIN 看到 key=NULL 就别挣扎了
只要执行计划里 key 是 NULL、type 是 ALL,说明 MySQL 已经明确拒绝使用索引。这时候:
• FORCE INDEX 无效,不是语法或权限问题,是结构硬限制
• 改统计信息、调 optimizer_switch、加复合索引都没用
• 字段上有函数(比如 UPPER(name))、隐式 collation 转换,会让情况更糟,但根源还是通配符位置
反向存储是唯一能绕过 B+ 树限制的方案
把原始字段反向存一份,让后缀匹配变成前缀匹配:
• 增加冗余列:ALTER TABLE users ADD COLUMN name_reversed VARCHAR(100) GENERATED ALWAYS AS (REVERSE(name)) STORED;
• 在该列建普通索引:CREATE INDEX idx_name_reversed ON users(name_reversed);
• 查询改写为:WHERE name_reversed LIKE CONCAT(REVERSE('abc'), '%')
• 注意:REVERSE() 在 MySQL 5.7+ 支持,8.0+ 性能更稳;生成列必须 STORED 才能建索引
FULLTEXT 不是万能解药,但适合中长文本
对标题、简介这类字段,MATCH(name) AGAINST('abc') 可行,但有硬门槛:
• ft_min_word_len 默认是 4,搜 “go”、“id” 这类短词直接返回空
• 必须重建索引才能生效:ALTER TABLE articles ADD FULLTEXT(name);
• AGAINST 里不能带通配符,'abc*' 是允许的,但 '*abc' 不支持
• 字段不能是表达式,也不能被函数包裹,否则索引失效
真正容易被忽略的是:哪怕你用了反向存储或 FULLTEXT,只要查询条件里混入了隐式类型转换(比如 name_reversed = 123)、或 collation 不一致(如字段是 utf8mb4_0900_as_cs,而字面量没声明),索引照样掉链子。性能问题从来不在 LIKE 本身,而在你怎么把它喂给引擎。


















