不能在JOIN的ON子句中直接用LIKE模糊关联,因前导%导致索引失效,引发全表扫描和性能崩溃;应优先通过精确字段JOIN后应用层过滤,或改用EXISTS、预计算映射、函数索引、全文索引等替代方案。

别在JOIN的ON子句里直接写LIKE做模糊关联——99%的场景下,它会拖垮查询,且索引完全失效。
为什么ON t1.name LIKE CONCAT('%', t2.keyword, '%')性能崩得快
数据库无法对含前导%的LIKE模式走B+树索引,JOIN时引擎被迫对左表每一行,都扫描整个右表做字符串比对。1万×1万就是1亿次比对,EXPLAIN里必然出现Using join buffer (Block Nested Loop)。更糟的是,t2.keyword为NULL时,CONCAT('%', NULL, '%')结果是NULL,整行匹配失败,你还查不到原因。
真正能用的替代方案,按优先级排序
把模糊逻辑从JOIN条件里“摘出来”,是唯一靠谱的起点:
- 先用精确字段缩小范围:比如
JOIN只基于city_id或category_id,再在应用层用str.contains()或filter()筛 - 必须SQL内完成?改用
WHERE或EXISTS:例如SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM logs l WHERE l.message LIKE CONCAT('%', u.name, '%') AND l.user_id = u.id),让优化器有机会用user_id索引定位 - 小配置表(如关键词表)可交叉连接后
WHERE过滤:FROM products p CROSS JOIN keywords k WHERE p.name LIKE CONCAT('%', k.keyword, '%'),但k.keyword必须带通配符(如'%apple%'),否则拼接逻辑仍危险
想保留JOIN结构,又想加速?绕开LIKE本身
核心思路是把“运行时模糊”变成“预计算确定性映射”:
- 建
alias_map表,人工或规则生成标准名→变体名映射,用=关联代替LIKE - MySQL 8.0+ 可建函数索引:
ALTER TABLE users ADD INDEX idx_name_reverse (REVERSE(name)),查询改写为REVERSE(name) LIKE REVERSE('%张三%') - 短文本相似度需求,用
LEVENSHTEIN()(PostgreSQL)或EDITDISTANCE()(SQL Server),但务必加LENGTH(t1.name) BETWEEN 5 AND 20等前置筛选,否则照样慢
全文索引不是银弹,用错就白搭
FULLTEXT只加速MATCH ... AGAINST,不能直接用于ON子句右侧字段。常见踩坑点:
- 字段类型不对:JSON、ENUM、TINYTEXT上建
FULLTEXT无效 - 没建索引就用:
ALTER TABLE logs ADD FULLTEXT(message)必须先执行 - JOIN中硬套:
JOIN logs ON MATCH(logs.message) AGAINST(u.name)语法错误,得先子查询预过滤 - PostgreSQL要装
pg_trgm扩展+GIN索引,且仅加速单表ILIKE,不参与JOIN选择率估算
最易被忽略的一点:哪怕你用了所有优化手段,只要业务要求实时匹配任意子串(如'%xxx%'),就注定无法避免全表扫描——这时候该考虑Elasticsearch或向量检索了。

















