like '%abc'一定不走索引,因b+树仅支持最左前缀匹配,以%开头无法定位起始位置,只能全表扫描;强制索引、改写sql或普通索引均无效,需用fulltext索引、函数索引或反向索引替代。

LIKE '%abc' 这类开头带 % 的查询,数据库压根不会走索引,优化全文索引或函数索引是唯一靠谱路径;硬加普通索引、改写 SQL 或 FORCE INDEX 都无效。
为什么 LIKE '%abc' 一定不走索引
MySQL(以及 PostgreSQL、SQL Server、Oracle)的 B+ 树索引只支持最左前缀匹配。LIKE '%abc' 要求从字符串任意位置匹配结尾,索引的有序结构完全失效。EXPLAIN 一定会显示 type: ALL 和 key: NULL,哪怕字段上有 INDEX(name) 也毫无作用。这不是配置问题,是索引机制本身的限制。
常见错误现象:
- 明明建了
INDEX(name),但WHERE name LIKE '%张三'执行慢得像全表扫描 - 用
FORCE INDEX强制指定索引,执行计划仍为type: ALL - 把条件改成
UPPER(name) LIKE UPPER('%abc'),索引直接失效(函数包裹)
全文索引(FULLTEXT)怎么建才有效
全文索引不是给所有模糊场景兜底的,它专为“关键词包含”设计,和 LIKE 的语义不同:不支持通配符前缀(*abc 合法,abc* 可用,但 *abc 报错),也不适合查编号、邮箱后缀这类结构化子串。
实操建议:
- 建表时加
FULLTEXT(title, content),不要 ALTER ADD(InnoDB 5.6+ 支持在线添加,但效率低) - 查询必须用
MATCH(title) AGAINST('销售报表' IN NATURAL LANGUAGE MODE),不能混用LIKE - 中文需启用
ngram插件(MySQL 5.7+),并设ft_ngram_token_size = 2,否则单字无法分词 - 停用词和最小词长会影响结果,可通过
ft_stopword_file和ft_min_word_len调整
函数索引替代后缀匹配(如邮箱域名)
对固定结尾的场景(如 email LIKE '%@qq.com'),全文索引大材小用,函数索引更轻量精准。
MySQL 8.0+ / PostgreSQL 可直接建:
CREATE INDEX idx_email_domain ON users ((SUBSTRING_INDEX(email, '@', -1)));
然后查:
SELECT * FROM users WHERE SUBSTRING_INDEX(email, '@', -1) = 'qq.com';
注意关键点:
- 函数必须出现在
INDEX定义中,不能只写在 WHERE 里(WHERE SUBSTRING(email, -7) = '@qq.com'仍全表扫) - PostgreSQL 还支持
CREATE INDEX ... USING gin(to_tsvector('zh', content))做中文全文 - MySQL 5.7 的虚拟列 + 普通索引是备选方案,但要
UPDATE全量数据,几百万行耗时明显
真正容易被忽略的细节
很多团队花时间调优 SQL,却漏掉两个硬性前提:
-
COLLATION不一致会隐式转换:字段是utf8mb4_0900_as_cs,而参数是utf8mb4_general_ci,索引就废了 - 字段类型与参数类型不匹配:比如
name VARCHAR(50)对比CONCAT('%', ?)传入 TEXT 类型,在某些 MySQL 版本触发隐式转换 - 没验证是否真走了索引:优化后必须跑
EXPLAIN FORMAT=JSON看used_key_parts和rows_examined_per_scan,不能只看“有 index”就以为成功










