like '%xxx'在join中性能崩溃,因左通配无法走索引,导致驱动表每行都触发被驱动表全表扫描,1万×1万=1亿次比对;on与where语义不同,前者影响连接结构,后者过滤结果集;替代方案应前置收敛或预计算映射。

JOIN里直接用LIKE做关联,为什么性能会崩
因为数据库无法对LIKE '%xxx'这种左通配模式走索引,JOIN时相当于对驱动表每一行,都去被驱动表全表扫描匹配。1万行 × 1万行就是1亿次字符串比对,哪怕加了索引也完全失效。
常见错误现象:LEFT JOIN t2 ON t1.name LIKE CONCAT('%', t2.keyword, '%')——一旦t2.keyword为空、含%或_,结果就不可控;更糟的是,优化器大概率放弃使用t1.name上的索引,改走全表扫描。
- 前缀匹配(
LIKE 'prefix%')可走索引,但必须确保字段没被函数包裹(如UPPER(name)) - 中缀或后缀匹配(
'%abc'、'%abc%')必然全表扫描 - MySQL 8.0+ 虽支持
REGEXP_LIKE()配合函数索引,但仅限简单模式,.*abc.*这类仍不走索引
ON子句里写LIKE和WHERE里写,区别到底在哪
本质区别在于语义层级:ON是定义“怎么连”,WHERE是定义“连完怎么筛”。放在ON里会影响连接结果集的结构,尤其对LEFT JOIN很关键。
实操建议:
- 若用
LEFT JOIN t2 ON t1.id = t2.t1_id AND t1.name LIKE '%北京%',t2为空时t1记录仍保留,但t1.name必须满足模糊条件才参与关联 - 若把
LIKE挪到WHERE里:LEFT JOIN t2 ON t1.id = t2.t1_id WHERE t1.name LIKE '%北京%',则先完成所有关联,再过滤——t1不满足条件的整行会被丢弃,LEFT JOIN退化为INNER JOIN - 动态关键词(如
t2.keyword)绝对不要放ON里做LIKE,除非你手动控制驱动表顺序且确认t2.keyword内容干净
想模糊关联又不拖慢查询,有什么轻量替代方案
核心思路是把“运行时模糊”变成“预计算确定性映射”。硬扛LIKE或REGEXP在JOIN里跑,基本等于放弃性能。
- 应用层过滤:先用精确条件(如
city_id、category)缩小结果集,再用Python/JS做str.contains()或正则过滤 - 冗余字段法:对
name字段预计算MD5(name)或SOUNDEX(name)存为新列,用等值JOIN加速 - 关键词拆分法:PostgreSQL可用
GENERATE_SERIES把t2.keywords按分隔符拆成行,转成等值JOIN;MySQL可建数字辅助表模拟 - 别迷信
REGEXP:它比LIKE开销高得多,每行都要启动正则引擎。只有真正需要多模式交替(如'(cat|dog)')或格式校验(如邮箱)时才值得用
REGEXP在JOIN中能替代LIKE吗
不能,而且风险更高。MySQL的REGEXP不支持索引加速,Oracle的REGEXP_LIKE()即使配合函数索引,也只对极简模式有效,且执行计划常显示FULL TABLE SCAN。
容易踩的坑:
-
REGEXP '北京'和LIKE '%北京%'语义等价,但前者慢3–5倍,毫无必要 -
REGEXP '^北京$'应直接用= '北京',REGEXP '^北京.*'应改用LIKE '北京%' - 多个
REGEXP条件叠加(如WHERE col REGEXP 'a' AND col REGEXP 'b'),数据量过万时响应明显卡顿 - MySQL中
REGEXP默认区分大小写,而LIKE通常不区分——混用时结果可能不一致
真正难搞的不是语法怎么写,而是当模糊逻辑嵌在JOIN里时,你根本没法靠加索引或调优hint救回来。要么前置收敛,要么换架构,没有中间路线。










