LIKE '%xxx'一定不走B+树索引,因B+树依赖有序前缀定位起点,而'%xxx'无确定起始位置,只能全表扫描;仅'xxx%'等前缀确定的模式才支持range扫描。

LIKE '%xxx' 为什么一定不走 B+ 树索引
不是 MySQL 故意跳过索引,是 B+ 树结构本身做不到。索引节点按字典序从左到右排列,查找必须能“定起点”。LIKE 'xxx%'可以跳到第一个'xxx'开头的记录,然后向右范围扫描;而LIKE '%xxx'要求结尾是'xxx',可能出现在'a-xxx'、'zzz-xxx'、'xxx'任意位置——起点完全不可知,优化器只能放弃索引,选全表扫描。
常见错误现象:EXPLAIN显示type: ALL、key: NULL,哪怕name字段有单列索引或在联合索引最左位,也完全不生效。数据量过 10 万后,查询从毫秒级拖到数秒甚至超时。
哪些 LIKE 写法能真正触发索引
只有一种本质条件:左侧前缀确定、无通配符干扰。是否走索引,和字段类型、索引长度、统计信息都无关,只看模式字符串本身。
-
LIKE '张%'→ ✅ 走索引(type: range),支持前缀匹配 -
LIKE '张_明'→ ✅ 走索引(_占一位,不影响前缀定位) -
LIKE '%张%'→ ❌ 全表扫描,前后都模糊,索引彻底失效 -
LIKE '张%明'→ ⚠️ 仅'张%'部分生效,'%明'不参与索引过滤
注意:LIKE后必须是字面量或参数化变量(如?或@p),不能是表达式,比如CONCAT('%', @kw)——这会导致隐式计算,索引照样失效。
中文场景下字符集和排序规则的“暗坑”
即使写成LIKE '张%',中文仍可能查不到或不走索引,根源常在字符集不一致:
- 表/列用
utf8mb4,但客户端连接用latin1→ 查询时发生隐式转换,索引列被CAST,索引失效 - 排序规则用
utf8mb4_general_ci,遇到“張”和“张”这种简繁差异,可能无法命中同一索引分支 - 建表未显式指定
COLLATE,依赖服务器默认值 → 不同环境行为不一致
验证方法:运行SHOW CREATE TABLE users;确认列定义含CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;再查连接变量:SHOW VARIABLES LIKE 'character_set%';和SHOW VARIABLES LIKE 'collation%';,确保client/connection/results三者一致。
真要支持后缀匹配,别硬改 SQL
试图靠加HINT、调统计信息、建复合索引、改REGEXP都没用。LIKE '%xxx'是 B+ 树的硬限制,不是 bug,也不是配置问题。
- 换索引类型:用
FULLTEXT索引(需MATCH AGAINST语法,且仅支持英文分词或中文ngram插件) - 换查询方式:把后缀匹配转为前缀匹配,例如对字段存
REVERSE(name),查时写WHERE reversed_name LIKE REVERSE('xxx') + '%' - 引入外部搜索引擎:如
Elasticsearch或Meilisearch,MySQL 只存原始数据
最容易被忽略的是:字符集和排序规则不一致引发的隐式转换,它不会报错,但会让EXPLAIN显示用了索引,实际性能却接近全表扫描。


















