暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JOIN 查询为什么慢?彻底搞懂驱动表与连接方式

解压泡泡糖 2025-04-24
102

你有没有遇到过这种情况:

两张表都不大,写了个 JOIN 查询,结果一查就慢得不行;
表加了索引,SQL 写得也没错,就是不走你想走的连接方式;
EXPLAIN 一看,rows 数百万,扫描行数爆炸!

这不是你 JOIN 用错了,而是你没真正理解驱动表、连接方式、执行顺序这些背后的执行细节。

今天我们就从底层讲清楚:

  • JOIN 的两张表谁先谁后,怎么决定?

  • 常见的连接方式有哪几种?谁更快?

  • EXPLAIN 怎么看 JOIN 的执行计划?

  • 写 JOIN 时如何控制性能的几个关键技巧


JOIN 到底谁先查?谁是驱动表?

MySQL 执行 JOIN 查询时,并不是“从左往右”执行,而是选择一张表作为“驱动表”,另一张作为“被驱动表”。

通俗理解:

  • 驱动表 = 外层循环(外层 for)

  • 被驱动表 = 内层循环(内层 for)

SQL 例子:

select * from orders o
join users u on o.user_id = u.id
where u.age > 30;

你以为它会先查 users
,但优化器可能反而选择先查 orders
,再拿 user_id 去关联查 users

所以,JOIN 的执行顺序完全取决于优化器的成本评估。


EXPLAIN 怎么看驱动顺序?

使用 EXPLAIN 分析 JOIN 查询时,会看到类似下面的输出:

id: 1
select_type: SIMPLE
table: users
type: ref
rows: 1200
Extra: Using where

id: 1
select_type: SIMPLE
table: orders
type: ref
rows: 5000
Extra: Using index

MySQL 会按照 id 和表出现的顺序,从上往下执行。

这里 users
 在前,说明是驱动表。

👉 驱动表就是 EXPLAIN 输出顺序中的第一张表。


三种常见的 JOIN 算法方式

MySQL 中有三种主要的 JOIN 执行方式:

1. Nested Loop Join(嵌套循环连接)

这是最常见也是默认的执行方式。

  • 外层循环扫描驱动表

  • 内层循环用索引去查被驱动表

适合:

  • 小表驱动大表

  • 被驱动表有索引(走 ref/index)

性能风险:驱动表大 + 被驱动表无索引,会产生暴力 N*N 扫描


2. Block Nested Loop Join(块嵌套)

MySQL 会把驱动表的部分结果放入临时表,然后一起 JOIN 被驱动表。适合 JOIN 数据量中等但不能一次性加载的场景。

MySQL 中不是显式支持,MySQL 8.0 才开始引入更智能的执行器可以做到。


3. Hash Join(哈希连接)

MySQL 在 8.0.18 开始引入 Hash Join,但不是默认开启。

适合:

  • 内存够大

  • JOIN 条件是等值(=)

  • 两张表都比较大,索引不适用

如果你用的是 MySQL 5.x 或早期 8.0,默认是不会用 Hash Join 的。


如何控制驱动表选择?

优化器默认会选择“更小、过滤性更高”的表作为驱动表,但你可以通过以下方式影响它的决策:

方式一:使用 STRAIGHT_JOIN

强制按写的顺序执行 JOIN:

select * from A
STRAIGHT_JOIN B on A.id = B.a_id

此时 A 就是驱动表,优化器不再重排顺序。


方式二:重写 WHERE 条件顺序

虽然不一定稳定,但在某些版本中,字段筛选顺序可能影响优化器评估的成本,导致驱动表变化。

建议:结合 EXPLAIN 验证每次改写是否生效。


JOIN 查询写得快的实用建议

  1. 驱动表要小、过滤性要高
    小表或 where 条件能快速缩小范围的表适合作为驱动表。

  2. 被驱动表的连接字段必须有索引
    否则内层循环就是全表扫描,非常慢。

  3. 避免 JOIN 三张以上的大表混查
    可考虑分步查询 + 临时表 + 结果合并。

  4. 避免 where + join 条件写混
    JOIN ON 写连接条件,WHERE 写过滤逻辑,清晰且便于优化器分析。

  5. LIMIT + JOIN 谨慎使用
    有时候 LIMIT 只是限制返回行,但执行仍然是全量 JOIN,需特别留意。


小结:JOIN 慢的背后,其实很可控

  • 谁是驱动表,谁是被驱动表,EXPLAIN 一眼就能看出来

  • 被驱动表没索引 = JOIN 性能灾难

  • STRAIGHT_JOIN 可以控制 JOIN 顺序

  • 多张表 JOIN,要关注执行顺序 + 连接方式 + 每一张表的 rows 扫描行数

不要只会写 JOIN,还要懂它是怎么执行的。这一步走对了,你的查询就赢在起跑线上。

文章转载自解压泡泡糖,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论