我需要使用Spark SQL或Dataframe API连接表。需要知道实现它的最佳方式。
情景是:
实现这一目标的最佳方法是什么?如果有人遇到类似问题,请分享您的经验。
答案 0 :(得分:8)
关于如何优化连接的默认建议是:
如果可以,请使用广播联接(请参阅this notebook)。从您的问题来看,您的表格似乎很大,并且广播联接不是一种选择。
考虑使用一个非常大的集群(你可能会认为它更便宜)。现在250美元(6/2016)在EC2现货实例市场上购买了大约24小时800核,6Tb RAM和许多SSD。在考虑大数据解决方案的总成本时,我发现人类往往会大大低估他们的时间。
使用相同的分区程序。有关共同分组联接的信息,请参阅this question。
如果数据量很大和/或您的群集无法增长,甚至上面的(3)导致OOM,请使用两遍方法。首先,重新分区数据并使用分区表(dataframe.write.partitionBy()
)保持不变。然后,在循环中串行连接子分区,“追加”到同一个最终结果表。
旁注:我上面说“追加”,因为在制作中我从不使用SaveMode.Append
。它不是幂等的,而且是危险的。我将SaveMode.Overwrite
深入到分区表树结构的子树中。在2.0.0和1.6.2之前,您必须删除_SUCCESS
或元数据文件,否则动态分区发现将会阻塞。
希望这有帮助。
答案 1 :(得分:2)
对源使用散列分区或范围分区进行分区,或者如果您对连接字段有更好的了解,则可以编写自定义分区。分区将有助于避免在连接期间重新分区,因为来自表的相同分区的火花数据将存在于同一位置。 ORC肯定会帮助这个事业。 如果这仍然导致溢出,请尝试使用比磁盘更快的快速
答案 2 :(得分:2)
Spark 使用 SortMerge joins 连接大表。它包括对两个表上的每一行进行散列,并将具有相同散列的行混洗到同一个分区中。键在两侧排序并应用 sortMerge 算法。据我所知,这是最好的方法。
要显着加快 sortMerges 的速度,请将大型数据集编写为带有预分桶和预排序选项(相同数量的分区)的 Hive 表,而不是平面拼花数据集。
namespace std {
template <>
struct hash<T> {
size_t operator()(const T& x) const {
return ...;
}
};
} // end of std
与收益相比,编写预分桶/预排序表的开销成本适中。
默认情况下,底层数据集仍将是镶木地板,但 Hive 元存储(可以是 AWS 上的 Glue 元存储)将包含有关表结构的宝贵信息。因为所有可能的“可连接”行都位于同一位置,Spark 不会对预先存储的表进行混洗(节省很大!),也不会对预先排序的表分区内的行进行排序。
tableA
.repartition(2200, $"A", $"B")
.write
.bucketBy(2200, "A", "B")
.sortBy("A", "B")
.mode("overwrite")
.format("parquet")
.saveAsTable("my_db.table_a")
tableb
.repartition(2200, $"A", $"B")
.write
.bucketBy(2200, "A", "B")
.sortBy("A", "B")
.mode("overwrite")
.format("parquet")
.saveAsTable("my_db.table_b")
查看使用和不使用预分桶的执行计划。
这不仅可以在加入过程中为您节省大量时间,而且可以在没有 OOM 的情况下在相对较小的集群上运行非常大的加入。在亚马逊,我们大部分时间都在生产中使用它(仍有少数情况下不需要)。
要了解有关预分桶/预分拣的更多信息: