LIKE 'abc%'有时不走索引,因字段类型需为VARCHAR/TEXT(CHAR补空格)、排序规则需匹配(如utf8mb4_0900_as_cs大小写敏感)、前缀区分度不足时优化器会弃用索引;'%xxx%'和'%xxx'无法利用B+树索引,须改用全文索引、反向索引或外部搜索方案。

MySQL 里模糊搜索不能只靠 LIKE 硬扛,尤其当出现 '%xxx%' 或 '%xxx' 时,基本等于放弃索引——EXPLAIN 显示 type: ALL 就是铁证。
为什么 LIKE 'abc%' 有时也不走索引?
前缀匹配看似能用索引,但实际命中依赖三个硬约束:
- 字段类型必须是
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(*)验证
FULLTEXT 索引不是 LIKE 的替代品,而是另一套系统
它不响应 WHERE ... LIKE,哪怕字段上有全文索引也完全无效。必须用 MATCH() AGAINST():
- 仅对
CHAR/VARCHAR/TEXT生效,INT或BLOB不支持 - 中文需显式指定
WITH PARSER ngram,否则默认最小词长为 4,搜“李”或“AI”直接被过滤 - 停用词(如“的”“了”)默认被忽略,查不到时先查
INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD - 布尔模式下支持
+/-控制词权重,但自然语言模式不支持通配符
反向索引只救 LIKE '%abc',不救 LIKE '%abc%'
建生成列 + 普通索引是目前最轻量、兼容性最好的方案:
- 执行
ALTER TABLE t ADD COLUMN name_rev VARCHAR(100) AS (REVERSE(name)) STORED; - 再建索引:
CREATE INDEX idx_name_rev ON t(name_rev); - 查询改写为:
WHERE name_rev LIKE 'cba%' - 注意:
LIKE '%abc%'反转后仍是'%cba%',前后都带通配符,B+ 树依然无法定位起点,此法无效
INSTR()、LOCATE()、POSITION() 全部白忙活
它们只是 LIKE '%xxx%' 的语法糖,执行计划照样是 type: ALL。MySQL 不会对函数结果自动建索引——除非你手动创建函数索引(MySQL 8.0+):
-
CREATE INDEX idx_name_instr ON t((INSTR(name, 'xxx')));这种写法无效,函数索引只支持确定性表达式,且必须是列的纯变换 - 真正可用的是:
CREATE INDEX idx_name_reverse ON t((REVERSE(name)));——但这也只等价于反向索引,解决不了中间匹配 - 别信“加个索引就能快”的说法,
EXPLAIN之前先看清楚type和key列
真正难啃的是 LIKE '%abc%' 场景:它天然排斥 B+ 树,强行优化只会绕远路。要么上全文索引(接受分词粒度和停用词限制),要么引入外部方案(如 Elasticsearch)。别在 MySQL 里试图用函数、冗余字段或分区表“蒙混过关”,效果有限还增加维护成本。


















