我有以下Apache Spark SQL连接谓词:
t1.field1 = t2.field1 and t2.start_date <= t1.event_date and t1.event_date < t2.end_date
数据:
t1 DataFrame have over 50 millions rows
t2 DataFrame have over 2 millions rows
t1.field1
DataFrame中的几乎所有t1
字段都具有相同的值(null
)。
目前,Spark群集在单个任务上挂起10分钟以上,以执行此连接,并且由于数据倾斜。在这个时间点上,只有一个工人和该工人上的一项任务起作用。其他所有9名工人都闲着。如何改善这种连接以便将负载从这一特定任务分配到整个Spark集群?
答案 0 :(得分:2)
我假设您正在进行内部联接。
可以按照以下步骤优化连接- 1.加入之前,我们可以根据最小或最大的start_date,event_date,end_date筛选出t1和t2。它将减少行数。
检查t2数据集的field1是否为空值,如果没有,则可以基于notNull条件过滤连接t1数据集。它将减小t1的大小
如果您的工作仅获得很少的执行程序,那么执行的分区数就会更少。只需对数据集重新分区,设置一个最佳数量,这样就不会出现大量分区,反之亦然。
您可以通过查看任务执行时间来检查分区是否正确发生(没有偏斜),应该类似。
检查执行程序的内存中是否可以容纳较小的数据集,可以使用broadcast_join。
答案 1 :(得分:2)
如果 t1 中的几乎所有行都具有 t1.field1 = null,并且 event_date 行是数字(或者您将其转换为时间戳),您可以先使用 Apache DataFu 进行范围连接,然后过滤掉 t1.field1 != t2.field1 之后的行。
范围连接代码如下所示:
t1.field1 = t2.field1 和 t2.start_date <= t1.event_date 和 t1.event_date 最后一个参数 - 10 - 是减少因子。正如 Raphael Roth 在他的回答中所建议的那样,这会进行分桶。 您可以在 the blog post introducing DataFu-Spark 中看到这种范围连接的示例。 完全披露 - 我是 DataFu 的成员并撰写了博文。t1.joinWithRange("event_date", t2, "start_date", "end_date", 10)
答案 2 :(得分:1)
我假设spark已经在t1.field1
上推送了非null过滤器,您可以在说明计划中进行验证。
我宁愿尝试创建一个可以用作等联接条件的附加属性,例如通过桶装。例如,您可以创建一个month
属性。为此,您需要在months
中枚举t2
,这通常是使用UDF完成的。有关示例,请参见此SO问题:How to improve broadcast Join speed with between condition in Spark