like '%xxx'一定不走索引,因b+树仅支持左前缀匹配,无法定位结尾匹配的起始位置,优化器直接选择全表扫描;而'xxx%'可重写为范围查询,能定位起始页并顺序遍历。

LIKE '%xxx' 为什么一定走不了索引
因为 B+ 树索引的物理结构决定了它只能高效支持「已知前缀」的范围查找。LIKE '%xxx' 或 LIKE '%xxx%' 没有确定的起始位置,优化器无法跳转到某一页开始扫描,只能全表扫——这不是 MySQL 的 bug,是索引数据结构本身的刚性限制。
EXPLAIN 看到 type=ALL 就说明失效了
执行 EXPLAIN SELECT * FROM users WHERE name LIKE '%张%'; 时,如果 key 列为空、type 是 ALL,就确认索引没用上。注意:即使 name 字段上有索引,只要通配符在开头,这个索引就形同虚设。
- 检查索引是否存在:
SHOW INDEX FROM users; - 确认字符集统一为
utf8mb4,否则可能因隐式转换导致key显示为NULL - 联合索引下,
name必须处于最左位置(如(name, status)),否则也无效
为什么 'xxx%' 能走索引而 '%xxx' 不行
优化器会把 name LIKE '华为%' 重写为 name >= '华为' AND name 这类等价范围查询,B+ 树能直接定位第一个「华为」开头的叶子页,然后顺序遍历。但 <code>'%华为' 没法推导出任何下界,数据库不知道该从哪一页开始读。
- 区分度太低也会让优化器放弃索引,比如
LIKE 'a%'匹配超过 25% 行,type可能仍是ALL - ORM 拼接时漏掉单引号(如写成
name LIKE %华为%)会导致语法错误或隐式类型转换,同样使索引失效
必须支持前缀模糊怎么办
硬加 B+ 树索引没用。真要查 '%xxx',得换方案:
- 建反向索引:
ALTER TABLE users ADD COLUMN name_reversed VARCHAR(100) AS (REVERSE(name)) STORED;,再对name_reversed建索引,查name_reversed LIKE 'xxx%' - 用全文索引:
ALTER TABLE users ADD FULLTEXT(name);,配合MATCH(name) AGAINST('xxx' IN NATURAL LANGUAGE MODE) - 中文场景优先考虑
utf8mb4_unicode_ci排序规则,避免因 collation 不一致导致匹配异常
真正容易被忽略的是字符集和排序规则的隐式不一致——哪怕表结构看着没问题,连接层或客户端配置错一个字符集,LIKE 就可能静默失效。











