我有一个大约2.4M行的数据集,每行有一个唯一的键。我在其他一些表上执行了一些复杂的SQL查询,生成了一个包含两列,一个键和值true
的数据集。该数据集大约有500行。现在我想(外部)将此数据集与原始表格连接起来。
这将生成一个具有非常稀疏值集的新表(大约500行为true,其他位置为null)。
最后,我想这样做大约200次,给我一个大约201列的最终表(关键,加上200个稀疏列)。
当我运行它时,我注意到它运行时它变得相当慢。第一次连接需要2秒,然后是4秒,然后是6秒,然后是10秒,然后是20秒,在大约30次加入后,系统永远不会恢复。当然,实际数字是无关紧要的,因为这取决于我正在运行的集群,但我想知道:
explain
表明每个联接变得越来越复杂(似乎包括所有以前的联接,它还包括复杂的SQL查询,即使这些查询已经checkpointed
) 。有没有办法真正检查点,所以每个连接只是一个连接?我实际上是在每次加入后调用show()
,所以我认为加入实际上是在那时发生的。答案 0 :(得分:2)
预计会减速吗
是的,在某种程度上是这样的。连接属于数据密集型系统中最昂贵的操作(声称线性可伸缩性的产品通常会从表中加入,这并非巧合)。分布式系统中的类似连接操作通常需要在节点之间进行数据交换,以达到一堆high latency numbers。
在Spark SQL中,还有计算执行计划的额外成本,其复杂程度大于线性。
我使用镶木地板作为数据存储格式(柱状存储),所以我希望添加更多列可以水平缩放,这是正确的假设吗?
没有。输入格式根本不会影响连接逻辑。
到目前为止我加入的所有列都不需要第N次加入,是否可以从内存中卸载?
如果真正排除在最终输出之外,他们将从执行计划中删除。但是既然你有理由,我认为并非如此,并且最终输出需要。
有没有办法真正检查点,所以每个连接只是一个连接?我实际上是在每次加入后调用show(),所以我认为连接实际上就是在那一点上发生的。
show
仅计算输出所需的一小部分数据。虽然可以重复使用随机播放文件,但它并不会缓存。
(似乎包括所有以前的连接,它还包括复杂的SQL查询,即使这些查询已被检查点)。
只有在完全计算数据并且不从执行计划中删除阶段时才会创建检查点。如果要显式地执行此操作,请将部分结果写入持久存储并在每次迭代开始时将其读回(这可能是一种过度杀伤)。
在spark中组合大量列时,我还能做些什么吗?
你能做的最好的事情就是找到一种完全避免连接的方法。如果key始终相同则单次shuffle,并且对组/分区(使用byKey
方法,窗口函数)的操作可能是更好的选择。
但是如果你
有一个大约2.4M行的数据集
然后使用支持就地修改的非分布式系统可能是更好的选择。
在最天真的实现中,您可以单独计算每个聚合,按键排序并写入磁盘。然后,数据可以逐行合并在一起,内存占用可忽略不计。