前缀百分号(LIKE '%关键词')导致索引失效,因B+树无法反向遍历;仅LIKE 'xxx%'等左侧确定前缀的写法可走索引;中文场景需统一字符集与排序规则;百万数据下全表扫描将拖垮数据库。

前缀百分号(LIKE '%关键词' 或 LIKE '%关键词%')直接导致索引失效,不是配置问题,也不是数据库版本缺陷,而是 B+ 树索引结构本身的限制。
为什么LIKE '%xxx'用不上索引?
B+ 树索引按字段值从左到右有序存储,查找必须能定位“起点”。LIKE 'xxx%' 可以从“xxx”开头的所有值中快速跳转并范围扫描;但 LIKE '%xxx' 要求匹配“结尾为 xxx”的任意长度字符串,索引无法反向遍历,只能逐行检查——优化器判定走全表扫描比索引回表更快,于是放弃索引。
常见错误现象:
- 执行
EXPLAIN SELECT * FROM users WHERE name LIKE '%明';显示type: ALL,key: NULL - 数据量超 10 万后,查询耗时从毫秒级飙升至数秒
- 即使
name字段有单列索引或在联合索引最左位,也完全不生效
LIKE位置调整:哪些写法能真正触发索引?
只有满足“已知左侧确定前缀”这一条件,B+ 树才能利用排序特性快速定位。是否走索引,只取决于通配符是否出现在左侧不可控位置。
-
LIKE '张%'→ ✅ 走索引(range类型),支持前缀匹配 -
LIKE '张_明'→ ✅ 走索引(range),下划线只占一位,不影响前缀定位 -
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 三者一致。
真要支持前后模糊,别硬扛,换技术栈
试图用 REVERSE(name) LIKE REVERSE('%张') + 函数索引绕过,或给每个字段加全文索引,实际落地成本高、维护难、中文分词效果差。
- MySQL 全文索引(
MATCH ... AGAINST)对中文支持弱,需配合ngram插件且最小分词长度固定(默认 2),'张'这种单字搜不到 - 函数索引(如
CREATE INDEX idx_rev_name ON users(REVERSE(name));)仅 PostgreSQL / Oracle 原生支持,MySQL 8.0+ 虽支持,但无法用于LIKE查询,只能配合=或IN - 真正可行的方案是剥离模糊检索逻辑:用 Elasticsearch 或 Meilisearch 承接
%关键词%类查询,主库只负责精确匹配和事务
最容易被忽略的一点:开发阶段用几百条测试数据看不出性能问题,一旦线上表达百万级,LIKE '%xxx%' 的查询会突然拖垮整个数据库连接池——别等报警才想起看执行计划。

















