LIKE 'abc%' 有时无法命中索引,核心原因在于以下几个条件必须同时满足:字段类型通常应为 VARCHAR/TEXT,排序规则需要兼容,例如在大小写敏感的场景中大小写必须完全对应,同时前缀本身还要具备足够的区分度;一旦区分度过低,MySQL 优化器往往会直接放弃索引扫描。这三个前提缺一不可,也是排查 MySQL LIKE 查询性能问题时最容易被忽略的关键点。

绝大多数 LIKE 查询性能慢,根本原因并不是 SQL 语法写得不够巧,而是根本没有走索引——尤其是 LIKE '%xxx' 或 LIKE '%xxx%' 这类写法,在 EXPLAIN 结果里,type 基本都会显示为 ALL,也就是全表扫描。
为什么 LIKE 'abc%' 有时也不走索引?
从原理上说,它具备使用 B+ 树索引的条件,但最终能否真正命中索引,还取决于下面三个硬性前提:
- 字段类型通常必须是
VARCHAR或TEXT;如果是CHAR类型,尾部会自动补空格,导致实际匹配更接近LIKE 'abc ',而不是你写的'abc%',从而影响索引使用效果 - 排序规则(collation)必须兼容:例如字段使用的是
utf8mb4_0900_as_cs(大小写敏感),那么WHERE name LIKE 'Abc%'就可能无法命中索引,通常要写成'abc%',或者显式指定COLLATE utf8mb4_0900_as_cs - 当前缀选择性太低时,优化器会主动放弃索引:比如
name LIKE 'A%'命中了全表 40% 的记录,MySQL 往往会判断全表扫描更快,因此key列依旧是NULL。可以通过SELECT COUNT(DISTINCT LEFT(name, 1)) / COUNT(*)来评估前缀选择性
LIKE '%abc' 怎么办?反向索引并不是万能方案
反向索引主要解决的是后缀匹配问题,并不适用于所有模糊查询场景:
- 先创建生成列:
ALTER TABLE users ADD COLUMN name_rev VARCHAR(100) AS (REVERSE(name)) STORED - 然后建立普通索引:
CREATE INDEX idx_name_rev ON users(name_rev) - 查询时改写为:
WHERE name_rev LIKE 'cba%'(注意:搜索关键词本身也要先反转) - 但对于
LIKE '%abc%',这种方法无能为力——因为反转以后仍然是LIKE '%cba%',前后依旧带通配符,本质上还是全表扫描 - 在 MySQL 8.0+ 中,函数索引可以简化写法:
CREATE INDEX idx_name_rev_func ON users((REVERSE(name))),但底层原理并没有变化,同样无法解决双向模糊匹配
什么时候该果断放弃 LIKE?
当业务场景明确需要使用 LIKE '%keyword%',并且数据量已经超过 10 万行时,继续依赖 MySQL 硬做模糊搜索,往往只会拖慢整条查询链路,甚至影响整体数据库性能:
FULLTEXT全文索引只对CHAR/VARCHAR/TEXT字段生效,而且必须通过MATCH() AGAINST()使用——如果仍然写成WHERE name LIKE '%abc%',即便表上已经建了全文索引,也不会生效- 中文搜索必须显式指定
WITH PARSER ngram,否则默认最小词长是 4,搜索“李”或“AI”这类短词时会被直接过滤;另外,停用词(如“的”“了”)也会被忽略,查不到时可以先查看INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD - 如果字段长度变化大、更新频繁,或者业务还需要高亮、分词、相关性排序等能力,那么
FULLTEXT很快也会遇到瓶颈——这时更适合引入 Elasticsearch、Meilisearch 等专业搜索引擎
很多开发者最容易忽略的,恰恰就是这一点:索引并不是建好了就一定会生效,只有当具体的 WHERE 条件、字段类型、排序规则(collation)以及数据分布这四个因素真正匹配时,索引才能被有效利用。排查 MySQL LIKE 查询是否走索引时,不能只看一个 EXPLAIN,更要结合 key、rows、filtered 这三列一起综合判断。
