like '%xxx'或'%xxx%'无法走索引,因b+树不支持后缀或中缀匹配;仅like 'abc%'在varchar字段、兼容排序规则、前缀选择性高时才可能走索引。

LIKE '%xxx' 或 '%xxx%' 基本没可能走索引,别白费劲加普通索引——EXPLAIN 里 type 一定是 ALL,不是你写法不对,是 B+ 树根本没法支持这种匹配。
什么时候 LIKE 'abc%' 才真能走索引?
前缀固定、后缀通配,只是必要条件,不是充分条件。三个实际卡点常被忽略:
-
VARCHAR字段才行:CHAR会自动补空格,LIKE 'abc%'可能匹配到'abc ',但索引值其实是'abc'(无空格),导致失效 - 排序规则必须兼容:用
utf8mb4_0900_as_cs这类大小写敏感 collation 时,WHERE name LIKE 'Abc%'不会命中name上的普通索引,得写成'abc%'或显式加COLLATE utf8mb4_0900_as_cs - 前缀区分度太低,优化器直接放弃:比如
name LIKE 'A%'匹配了全表 35% 行,MySQL 认为扫表更快,key列仍为NULL。可用SELECT COUNT(DISTINCT LEFT(name, 1)) / COUNT(*)验证选择性
LIKE '%xxx' 怎么办?反向索引不是万能的
建 name_rev VARCHAR(100) AS (REVERSE(name)) STORED + 普通索引,查询改写成 WHERE name_rev LIKE 'xxx%',确实能解决后缀匹配。但注意边界:
- 只对
LIKE '%xxx'有效;LIKE '%xxx%'反转后还是LIKE '%xxx%',毫无意义 - 字段长度不能超限制:MySQL 生成列最大长度受
max_length_for_sort_data和存储引擎限制,超长文本可能截断 - 写入开销增加:每次 INSERT/UPDATE 都要计算
REVERSE()并存一份冗余数据,TPS 高的表要测压
全文索引不是开关一开就快,中文尤其容易踩坑
MATCH() AGAINST() 和 LIKE 完全不兼容——哪怕你建了 FULLTEXT 索引,WHERE name LIKE '%xxx%' 也绝不会用它。真正要用,得满足:
- 字段类型只能是
CHAR/VARCHAR/TEXT;JSON或带函数包装的列不行 - 中文必须显式指定
WITH PARSER ngram:否则默认最小词长是 4,搜'李'或'AI'直接被过滤掉 - 停用词会静默丢弃:查
'数据库的优化','的'被当停用词跳过,实际只搜'数据库 优化'。先查INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD确认列表
INSTR()、LOCATE()、POSITION() 真的不提速
它们和 LIKE '%xxx%' 是同一类执行路径,都是全表扫描。MySQL 不会对函数结果自动建索引,除非你手动创建函数索引(MySQL 8.0+):
ALTER TABLE users ADD INDEX idx_name_instr ((INSTR(name, 'xxx')));
但注意:函数索引只加速 WHERE INSTR(name, 'xxx') > 0 这种写法,且无法支持排序或范围查询;INSTR() 返回位置,但索引只存 0 或非 0,实际效果有限。
真正难处理的是 LIKE '%xxx%' —— 它既不能靠反转转化,又不适合全文索引(短词、含停用词、需精确位置),这时候该考虑的不是 SQL 优化,而是换技术栈:ES、ClickHouse 或专用文本搜索引擎。











