Spark Join添加OR子句触发笛卡尔积执行计划的原因与正确性咨询
Spark无序键Join问题解答
场景说明
在Spark 3.1.2版本下进行数据处理,存在两个DataFrame:
- df1表结构与样例数据:
+---+---+ | a| b| +---+---+ | 1| 2| | 1| 3| | 1| 4| | 2| 5| | 2| 6| | 3| 7| | 3| 8| +---+---+
- info表结构与样例数据,其中(a,b)字段无顺序含义:
+---+---+------------+ | a| b| i| +---+---+------------+ | 1| 2|1 --> 2 info| | 1| 3|1 --> 3 info| | 7| 3|3 --> 7 info| +---+---+------------+
需求为df1的每一行按无序(a,b)匹配info表的对应行,原始等值Join只能匹配顺序一致的行,修改Join条件增加OR子句后结果符合预期,但执行计划变为笛卡尔积。
问题解答
1. OR子句写法的逻辑正确性
在你给出的前提(df1和info表的(a,b)组合均唯一)下,该写法逻辑完全正确:
- 当info表(a,b)顺序与df1一致时,
(df1.a = info.a and df1.b = info.b)条件命中匹配 - 当info表(a,b)顺序与df1相反时,
(df1.a = info.b and df1.b = info.a)条件命中匹配
不存在重复匹配、漏匹配的问题,运行结果符合业务需求。
2. 触发笛卡尔积的原因
Spark的主流高效Join算法(SortMergeJoin、BroadcastHashJoin、ShuffleHashJoin)都要求Join条件是统一的等值映射条件,可以基于固定的Join key做数据分区、排序操作,避免全量行两两匹配。
当使用OR连接两组不同的等值条件后,Join条件不存在统一的匹配key:第一组条件的info侧匹配key是(a,b),第二组条件的info侧匹配key是(b,a),Spark无法找到可以同时满足两组条件的公共分区/排序规则,只能退化为笛卡尔积执行:先将两个表的所有行做全量两两组合,再逐行过滤符合OR条件的行,因此出现你看到的CartesianProduct执行计划。
性能优化方案
若两个表数据量较大,笛卡尔积会带来极高的性能开销,可以通过如下改写避免笛卡尔积,回归高效的SortMergeJoin执行计划:
select df1.*, info_union.i from df1 join ( -- 合并info表原始(a,b)和交换顺序后的(b,a)数据,统一Join key select a, b, i from info union all select b as a, a as b, i from info ) info_union on df1.a = info_union.a and df1.b = info_union.b
内容的提问来源于stack exchange,提问作者Vitaliy
相关产品推荐
相关产品推荐

