坚持比非坚持呼叫慢

时间:2017-09-07 16:52:14

标签: apache-spark-sql spark-dataframe

我的设置是:3节点YARN群集上的Spark 2.1,具有160 GB,48个vcores。 动态分配已开启。 spark.executor.memory=6Gspark.executor.cores=6

首先,我正在阅读蜂巢表:订单(329MB)和lineitems(1.43GB)和 做左外连接。 接下来,我根据连接应用了7种不同的过滤条件 数据集(类似var line1 = joinedDf.filter("linenumber=1")var line2 = joinedDf.filter("l_linenumber=2")等)。 因为我多次对已连接的数据集进行过滤,所以我认为执行持久化(MEMORY_ONLY)会有所帮助,因为已连接的数据集将完全适合内存。

  1. 我注意到,通过持久化,Spark应用程序运行时间比没有持续时间长(3.5分钟对3.3分钟)。通过持久化,DAG显示为持久性创建了单个阶段,而其他下游作业正在等待持久性完成。 这是否意味着持续存在是一种阻止呼叫?或者当持久性块可用时,其他作业中的阶段是否开始处理?

  2. 在非持久化案例中,不同的作业正在创建不同的阶段来读取相同的数据。数据在不同的阶段被多次读取,但这仍然比持续的情况更快。

  3. 对于较大的数据集,persist实际上会导致执行程序耗尽 内存(Java堆空间)。没有坚持,Spark工作就完成了。我在这里查看了其他一些建议:Spark java.lang.OutOfMemoryError: Java heap space。 我尝试增加/减少执行程序核心,坚持 仅限磁盘,增加分区,修改存储比率,但似乎没有任何帮助执行程序内存问题。

  4. 如果有人能提到持久性是如何工作的,我会很感激,在什么情况下它比不持久更快,更重要的是,如何解决内存不足问题。

1 个答案:

答案 0 :(得分:1)

我建议您阅读spark中transformationsactions之间的区别。我必须承认,我已经多次被自己咬过了。

火花中的数据被懒惰地评估,这基本上意味着在行动之前没有任何事情发生。执行。 .filter()函数是一个转换,因此当代码到达该点时实际上没有任何事情发生,除了向转换管道添加一个部分。对.persist()的调用行为方式相同。

如果您.persist()来电的下游代码有多个可以同时触发的操作,那么很可能您实际上已经"坚持"单独的每个操作的数据,以及耗尽内存(Spark UI中的"存储'选项卡将告诉您数据集的缓存百分比,如果它超过100%缓存,那么你正在看我在这里描述的内容)。更糟糕的是,您可能永远不会真正使用缓存数据。

通常,如果您在代码中有一个点,其中数据集分为两个单独的转换管道(在您的示例中为每个或单独的.filter()),.persist()是一个好主意,以防止多次读取数据源,和/或在fork之前保存昂贵的转换管道的结果。

很多时候,在.persist()调用之后(在数据分支之前)触发单个操作是个好主意,以确保后续操作(可能同时运行)从持久缓存中读取,而不是而不是独立评估(并无用地缓存)数据。

TL; DR:

joinedDF.count()之后,.persist()之前.filter()。{/ p>