like查询慢的本质是索引无法跳过全表扫描;当出现前导通配符(如’%abc’或’%abc%’)时,b+树索引失去有序性优势,只能退化为索引扫描或全表扫描,仅’abc%’等左匹配可走范围查询。

LIKE 查询为什么慢?本质是索引无法跳过全表扫描
当 WHERE name LIKE '%abc' 或 WHERE name LIKE '%abc%' 出现时,mysql 通常无法使用 B+ 树索引的有序性做快速定位,只能从索引最左前缀开始逐条比对,最终退化为索引扫描(index scan)甚至全表扫描(table scan)。只有 LIKE 'abc%' 这种左匹配才可能走索引范围查询。
哪些 LIKE 写法能用上索引?看前导通配符是否存在
能否命中索引,关键看通配符是否出现在模式最左侧:
-
WHERE col LIKE 'abc%'→ 可用索引,B+ 树按字典序查找以'abc'开头的所有值 -
WHERE col LIKE 'ab_c'→ 可用索引(下划线只占一位),仍属于左匹配前缀 -
WHERE col LIKE '%abc'→ 索引失效,必须倒序扫描,MySQL 不支持反向索引遍历 -
WHERE col LIKE '%abc%'→ 索引失效,无有效起始点 -
WHERE col LIKE 'ab%c'→ 索引部分生效,只加速到'ab'前缀,后续仍需字符串匹配
除了改写 LIKE,还有哪些实际可行的优化手段?
不能改 SQL?那就绕开 LIKE。常见且有效的替代路径有:
- 用全文索引 +
MATCH ... AGAINST()替代模糊匹配,尤其适合中文需配合ngram插件或MeCab - 对固定长度后缀查询(如查文件扩展名),可新增计算列:
ALTER TABLE t ADD column ext VARCHAR(10) STORED AS (SUBSTRING_INDEX(filename, '.', -1)),再对ext建索引 - 将高频模糊搜索字段同步到 elasticsearch 或 RedisSearch,由专用搜索引擎承担
- 如果业务允许,把
LIKE '%keyword%'拆成“关键词前置”+“关键词后置”两个条件并集,再用覆盖索引减少回表
如何确认你的 LIKE 查询到底有没有走索引?
别猜,用 EXPLAIN 看执行计划,重点关注这几项:
-
type字段:出现ALL表示全表扫描;range或ref才算走了索引 -
key字段:显示实际使用的索引名,为空即未命中 -
rows字段:预估扫描行数,远大于结果集数量就是低效信号 - 注意隐式转换:比如
col VARCHAR(50)被与整数比较(WHERE col = 123),会导致索引失效,LIKE 同理 —— 若字段是 utf8mb4_bin 排序,但查询用的是非二进制 collation,也可能触发隐式转换
真正容易被忽略的是字符集和排序规则不一致引发的隐式转换,它不会报错,但会让 EXPLAIN 显示用了索引,实际性能却接近全表扫描。