左模糊匹配(like '%xxx')必然导致b+树索引失效,因其无法确定扫描起点;可用方案包括小表加覆盖索引、全文索引或外置搜索引擎。

左模糊匹配(LIKE '%xxx')在 MySQL 中必然导致 B+ 树索引失效,这不是配置或写法问题,而是索引结构决定的刚性限制——没有绕过去的“优化技巧”,只有换思路。
为什么 LIKE '%xxx' 一定不走索引?
B+ 树索引按字段值字典序存储,只支持从某个确定前缀开始向后遍历。LIKE '%xxx' 要找所有“以 xxx 结尾”的记录,起点完全不确定,数据库无法跳转到索引某一页就开始扫描,只能全表或全索引扫描。EXPLAIN 里一定会看到 type = ALL 或 type = index,key 字段为空。
常见误判点:
- 以为加了索引就“应该能用”——其实索引存在 ≠ 查询能命中
- 看到
EXPLAIN显示key非空就以为生效——要同时看type是不是range或ref - 在 ORM 里拼成
name LIKE %xxx(漏单引号)→ 触发隐式类型转换,key直接变NULL
必须支持后缀匹配时的三个真实可用路径
别在 SQL 层硬扛,优先按数据量和业务容忍度选方案:
-
小表兜底(≤ 5 万行):加
Covering Index减少回表,强制LIMIT 100防拖垮,例如CREATE INDEX idx_name_email ON t (name, email),再查SELECT name, email FROM t WHERE name LIKE '%华为' LIMIT 100 -
全文索引(MySQL 5.6+,适合短文本):建
FULLTEXT(name),用MATCH(name) AGAINST('华为' IN NATURAL LANGUAGE MODE);注意中文需 MySQL ≥ 8.0.30 +ngram,否则默认按字切分,“华为手机”搜不出“华为” -
外置搜索引擎(标准解法):把
name同步到 Elasticsearch,建keyword类型字段,查q=name:华为,响应稳定在 20ms 内,且天然支持拼音、同义词、高亮
反转字段方案的实际坑点
有人试过 ALTER TABLE t ADD COLUMN name_rev VARCHAR(100) STORED AS (REVERSE(name)) 再建索引查 name_rev LIKE 'aihw%',理论上可行,但线上几乎没人用:
-
REVERSE(name) LIKE '%华为'依然不走索引——函数索引只对右模糊有效 - 写入时要维护反转列,触发器或应用层双写,出错概率翻倍
- 多数 ORM 不识别函数索引,生成的 SQL 仍会忽略它
- MySQL 5.7 占比仍高,
8.0+函数索引实际覆盖率不足 30%
最容易被忽略的性能根源:没做数据分层
真正卡住的往往不是查询本身,而是把搜索请求全压给主库。高频后缀查询(如手机号后四位、订单号末尾)应拆出来:用冗余字段(如 mobile_last4)+ 普通索引,或走 Redis 的 SCAN + KEYS(仅限小规模),甚至直接缓存结果集。模糊查询性能问题,90% 出在没意识到:MySQL 不是搜索引擎,该交出去的,就别留在 SQL 层里扛。











