LIKE查询慢的本质是索引失效:以%开头(如'%abc')导致B+树无法利用有序性而全表扫描;仅'abc%'等固定前缀形式可走索引。

LIKE 查询为什么慢?本质是索引失效
当 LIKE 模式以 % 开头(如 WHERE name LIKE '%abc'),MySQL 无法使用 B+ 树索引的有序性,只能全表扫描。哪怕 name 字段有索引,也会被跳过。这是性能问题的根源,不是写法“不够巧”能绕开的。
真正有效的优化方向只有两个:避免前导通配符、或改用支持前缀匹配的索引结构。
哪些 LIKE 能走索引?看模式是否可利用最左前缀
只有满足“固定前缀 + 可选后缀”的形式,B+ 树索引才可能生效:
-
WHERE name LIKE 'abc%'→ ✅ 可走索引(等价于name >= 'abc' AND name ) -
WHERE name LIKE 'ab%c'→ ❌ 不行(中间有通配符,无法确定范围边界) -
WHERE name LIKE '%abc'→ ❌ 全表扫描(无法定位起始位置) -
WHERE name LIKE 'a%b%c'→ ❌ 同样失效(非连续前缀)
注意:LIKE 是否区分大小写取决于字段排序规则(collation),utf8mb4_0900_as_cs 区分,utf8mb4_0900_ai_ci 不区分——但不影响索引是否命中。
前导 % 怎么办?全文索引 or 倒排索引思路
如果业务确实需要“包含任意子串”搜索(如用户搜“数据库”想匹配“MySQL数据库教程”),LIKE '%...%' 是刚性需求,那就得换技术方案:
- 对短文本(如标题、标签),用
FULLTEXT索引 +MATCH ... AGAINST,支持自然语言和布尔模式,但不支持单字符或纯通配符 - 对长文本或需高精度匹配,预处理字段:比如把
content拆成所有长度为 2–4 的子串,存入关联表,查询时查子串索引(类似 n-gram) - 用
GENERATED COLUMN存反向字符串并建索引,解决LIKE '%abc'类需求:ALTER TABLE t ADD COLUMN name_rev VARCHAR(100) AS (REVERSE(name)) STORED;</code><br><code>CREATE INDEX idx_name_rev ON t(name_rev);</code><br>查 <code>'%abc'</code> 就变成 <pre class="brush:php;toolbar:false;">WHERE name_rev LIKE 'cba%'
其他容易被忽略的坑
很多优化尝试失败,是因为踩了这些细节:
CHAR 字段会自动补空格,<code>LIKE 'abc '可能意外命中,建议统一用VARCHAR-
COLLATION影响比较逻辑,utf8mb4_unicode_ci会让LIKE 'cafe'匹配café,但索引仍可用;而_bin排序规则下大小写敏感,别误以为“没走索引” -
EXPLAIN看type是range或ref才算走索引,ALL就是全表扫——别只看有没有key字段 - 函数包裹字段直接禁用索引:
WHERE UPPER(name) LIKE 'ABC%'永远不走索引,应改为WHERE name LIKE 'abc%' COLLATE utf8mb4_0900_as_cs
模糊查询没有银弹。决定用哪种方式,得先明确:要的是“开头像”“中间含”还是“结尾像”,再选对应索引策略。否则加再多索引也没用。


















