LIKE查询易全表扫描,因B+树仅支持左前缀匹配;'%abc'或'%abc%'无法定位起始位置,优化器必弃索引,EXPLAIN显示type=ALL、key=NULL。

LIKE 查询为什么容易触发全表扫描
MySQL 对 LIKE 的索引使用非常敏感,只有当模式不以通配符开头时(即不以 % 或 _ 开头),才能有效利用 B+ 树索引的有序性。一旦写成 LIKE '%abc' 或 LIKE '%abc%',优化器就无法从索引树根节点向下定位起始位置,只能放弃索引、遍历全表。
常见错误现象包括:查询响应明显变慢、EXPLAIN 显示 type=ALL、key=NULL、rows 接近表总行数。
关键判断依据是:是否能利用索引的「最左前缀匹配」特性。
哪些 LIKE 模式能走索引
能用上索引的 LIKE 模式必须满足「左侧固定」——即通配符只出现在右侧或中间,但不能在最左边。
-
LIKE 'abc%':可走索引,等价于范围查询 WHERE col >= 'abc' AND col
-
LIKE 'abc%def':仍可走索引(如索引长度足够),MySQL 会先定位到 'abc' 前缀,再在索引页内过滤
-
LIKE 'ab_c':下划线只匹配单字符,且左侧固定,也能用索引(前提是 _ 不被转义为字面量)
注意:LIKE 'abc\_def' 中的反斜杠必须配合 ESCAPE 子句才生效,否则 \_ 就是字面下划线,不参与通配;而 LIKE 'abc_def'(无转义)会把 _ 当作通配符,此时若字段值为 'abcXdef' 也能命中,但依然能走索引。
如何确认你的 LIKE 查询是否走了索引
别猜,直接看执行计划。运行 EXPLAIN SELECT ... LIKE ...,重点关注三列:
-
key:非 NULL 表示用了哪个索引
-
type:应为 range(前缀匹配)或 ref(等值+范围混合),而非 ALL 或 index
-
possible_keys 和 key_len:结合字段定义判断索引实际用了几字节(例如 key_len=12 对应 VARCHAR(10) utf8mb4 字段用了全部长度)
如果 key 是 NULL,但你知道字段有索引,大概率是模式以 % 开头,或者存在隐式类型转换(比如对数字字段查 LIKE '123%',而字段是 INT,会触发全表转换)。
替代方案:当必须左模糊时怎么避免全表扫描
没有银弹,但有更可控的折中路径:
- 用倒序存储 + 正向查询:对字段维护一个
REVERSE(col) 的生成列,并为其建索引,查询 LIKE '%abc' 改为 REVERSE(col) LIKE 'cba%'
- 用全文索引(
FULLTEXT):适合中文需分词、英文自然语言场景,但不支持模糊前缀,且仅 MyISAM/InnoDB(5.6+)支持,语法是 MATCH(col) AGAINST('abc*' IN BOOLEAN MODE)
- 引入外部搜索引擎:Elasticsearch 或 Meilisearch 更适合复杂模糊、拼音、错别字等需求,MySQL 本身不是为这类查询设计的
- 加覆盖索引减少回表:即使
LIKE 无法走索引,若查询只涉及索引字段,至少避免磁盘随机读(Extra 中出现 Using index)
真正容易被忽略的是字符集影响:utf8mb4 下一个汉字占 4 字节,若索引长度设为 INDEX(col(191)),而字段是 VARCHAR(255),那超过 191 字节的部分永远无法被索引覆盖——这时候哪怕写 LIKE 'abc%',也可能因索引截断而失效。
LIKE 'abc%':可走索引,等价于范围查询 WHERE col >= 'abc' AND col
LIKE 'abc%def':仍可走索引(如索引长度足够),MySQL 会先定位到 'abc' 前缀,再在索引页内过滤LIKE 'ab_c':下划线只匹配单字符,且左侧固定,也能用索引(前提是 _ 不被转义为字面量)EXPLAIN SELECT ... LIKE ...,重点关注三列:
-
key:非NULL表示用了哪个索引 -
type:应为range(前缀匹配)或ref(等值+范围混合),而非ALL或index -
possible_keys和key_len:结合字段定义判断索引实际用了几字节(例如key_len=12对应VARCHAR(10)utf8mb4 字段用了全部长度)
key 是 NULL,但你知道字段有索引,大概率是模式以 % 开头,或者存在隐式类型转换(比如对数字字段查 LIKE '123%',而字段是 INT,会触发全表转换)。
替代方案:当必须左模糊时怎么避免全表扫描
没有银弹,但有更可控的折中路径:
- 用倒序存储 + 正向查询:对字段维护一个
REVERSE(col) 的生成列,并为其建索引,查询 LIKE '%abc' 改为 REVERSE(col) LIKE 'cba%'
- 用全文索引(
FULLTEXT):适合中文需分词、英文自然语言场景,但不支持模糊前缀,且仅 MyISAM/InnoDB(5.6+)支持,语法是 MATCH(col) AGAINST('abc*' IN BOOLEAN MODE)
- 引入外部搜索引擎:Elasticsearch 或 Meilisearch 更适合复杂模糊、拼音、错别字等需求,MySQL 本身不是为这类查询设计的
- 加覆盖索引减少回表:即使
LIKE 无法走索引,若查询只涉及索引字段,至少避免磁盘随机读(Extra 中出现 Using index)
真正容易被忽略的是字符集影响:utf8mb4 下一个汉字占 4 字节,若索引长度设为 INDEX(col(191)),而字段是 VARCHAR(255),那超过 191 字节的部分永远无法被索引覆盖——这时候哪怕写 LIKE 'abc%',也可能因索引截断而失效。
REVERSE(col) 的生成列,并为其建索引,查询 LIKE '%abc' 改为 REVERSE(col) LIKE 'cba%'
FULLTEXT):适合中文需分词、英文自然语言场景,但不支持模糊前缀,且仅 MyISAM/InnoDB(5.6+)支持,语法是 MATCH(col) AGAINST('abc*' IN BOOLEAN MODE)
LIKE 无法走索引,若查询只涉及索引字段,至少避免磁盘随机读(Extra 中出现 Using index)


















