LIKE '%xxx'在JOIN中必然触发全表扫描,因B+树索引仅支持最左前缀匹配,无确定起始位置导致优化器无法利用索引,执行计划中type=ALL、key=NULL、rows≈总行数即为证据。

LIKE '%xxx'在JOIN中必然触发全表扫描
因为B+树索引只支持最左前缀匹配,LIKE '%xxx'没有确定的起始位置,优化器无法跳转到索引某一段,只能对被驱动表逐行扫描。执行计划里type显示ALL、key为NULL、rows接近总行数,就是这个现象的直接证据。
嵌套循环JOIN让性能问题指数级放大
MySQL用嵌套循环算法执行JOIN:驱动表每1行,都要完整遍历被驱动表所有行做LIKE比对。若驱动表1万行、被驱动表1万行,理论比对次数是1亿次——这还没算字符串匹配本身的CPU开销。
- 即使被驱动表字段有索引,
LIKE '%xxx'也会让索引失效 - 驱动表选错(比如拿大表当驱动表)会让扫描量直接翻几个数量级
-
LEFT JOIN中,ON t1.name LIKE CONCAT('%', t2.keyword, '%')还会因t2.keyword为空或含%、_导致逻辑错误,不是“没匹配上”,而是整行被丢弃
函数包裹、隐式转换进一步关闭索引通道
哪怕你写的是LIKE 'abc%',只要字段被函数处理过,索引就作废。常见踩坑点包括:
-
UPPER(name) LIKE 'ABC%'→ 索引失效 -
name LIKE CONCAT('%', ?)→ 参数为空时变成LIKE '%%',强制全表扫 - 字段是
utf8mb4_0900_as_cs而连接字符集是utf8mb4_general_ci→ COLLATION不匹配,索引失效 -
INSTR(name, ?) > 0或LOCATE(?, name) > 0→ 和LIKE '%xxx%'一样,仍是全表扫描
ON和WHERE里写LIKE,语义完全不同
放在ON里是定义“怎么连”,影响连接结构;放在WHERE里是“连完再筛”,会把LEFT JOIN退化成INNER JOIN。例如:
-
LEFT JOIN t2 ON t1.id = t2.t1_id AND t1.name LIKE '%北京%'→t1不满足模糊条件的行,t2字段全为NULL,但t1行仍保留 -
LEFT JOIN t2 ON t1.id = t2.t1_id WHERE t1.name LIKE '%北京%'→ 先完成所有关联,再过滤,t1不满足条件的整行被丢弃
很多人调了半天发现结果变少,其实只是把条件从ON挪到了WHERE,没意识到语义已变。

















