InnoDB 中 LIKE '%xxx' 不走索引是因 B+Tree 无法支持左模糊匹配,而 MyISAM “看似快”实因其无事务开销;真正有效的优化方案只有全文索引、外部搜索引擎或业务层反向字段。

为什么LIKE '%xxx' 在 InnoDB 里不走索引而 MyISAM 有时“看起来快”
不是 InnoDB 故意慢,而是它更严格地遵守 B+Tree 索引的匹配规则。MyISAM 的 KEY 是独立于数据的哈希/前缀结构,某些旧版本对 LIKE 'xxx%' 做了特殊优化(比如前缀压缩 + 快速跳过),但对 LIKE '%xxx' 同样无效;所谓“MyISAM 更快”,往往是因为它没开事务、没行锁、没 MVCC,查询时直接读磁盘页,少了并发控制开销——但这在高并发写场景下会崩。
INNODB 中 LIKE 查询不走索引的典型触发条件
只要出现以下任一情况,EXPLAIN 中的 key 字段就会是 NULL,哪怕字段上有索引:
-
WHERE name LIKE '%abc'—— 左模糊,B+Tree 无法从根节点向下定位起始位置 -
WHERE UPPER(name) LIKE 'ABC%'—— 函数包裹字段,优化器无法下推索引扫描 -
WHERE name LIKE 'abc%' ESCAPE '\'但未定义ESCAPE字符或转义逻辑错乱 - 字段类型与参数类型不一致,例如
name VARCHAR(50)对应查询条件传入数字123,触发隐式转换
真正能提速的替代方案,不是“强行加索引”
对 LIKE '%xxx' 类需求,B+Tree 天然无解。别在原字段上硬建普通索引,那只会拖慢写入。可行路径只有三条:
- 改用
FULLTEXT索引(仅限CHAR/VARCHAR/TEXT):支持自然语言模式和布尔模式,但要求最小词长(默认 4),且不支持纯数字或短拼音匹配 - 引入外部搜索引擎:Elasticsearch 或 Meilisearch 做异步同步,适合用户搜索、日志检索等场景;注意主键回查延迟和一致性窗口
- 业务层前置生成反向字段:例如原始字段
title,额外维护title_reversed并建索引,把LIKE '%mysql'改成WHERE title_reversed LIKE 'lqsym%'—— 这招在固定后缀匹配(如文件扩展名、域名后缀)中很稳
容易被忽略的陷阱:排序 + 模糊查询 = 文件排序暴雷
当语句类似 SELECT * FROM article WHERE content LIKE '%bug%' ORDER BY created_at DESC LIMIT 20,即使你给 content 加了 FULLTEXT,ORDER BY 仍可能触发 Using filesort,因为全文索引不保证结果按时间有序。此时必须加覆盖索引:ALTER TABLE article ADD INDEX idx_content_time (content(100), created_at) —— 注意 content(100) 是前缀长度,不能省略,否则索引过大;且该索引对 LIKE '%bug%' 依然无效,只对 LIKE 'bug%' 有效。


















