我的设置是:3节点YARN群集上的Spark 2.1,具有160 GB,48个vcores。
动态分配已开启。
spark.executor.memory=6G
,spark.executor.cores=6
首先,我正在阅读蜂巢表:订单(329MB)和lineitems(1.43GB)和
做左外连接。
接下来,我根据连接应用了7种不同的过滤条件
数据集(类似var line1 = joinedDf.filter("linenumber=1")
,var line2 = joinedDf.filter("l_linenumber=2")
等)。
因为我多次对已连接的数据集进行过滤,所以我认为执行持久化(MEMORY_ONLY
)会有所帮助,因为已连接的数据集将完全适合内存。
我注意到,通过持久化,Spark应用程序运行时间比没有持续时间长(3.5分钟对3.3分钟)。通过持久化,DAG显示为持久性创建了单个阶段,而其他下游作业正在等待持久性完成。 这是否意味着持续存在是一种阻止呼叫?或者当持久性块可用时,其他作业中的阶段是否开始处理?
在非持久化案例中,不同的作业正在创建不同的阶段来读取相同的数据。数据在不同的阶段被多次读取,但这仍然比持续的情况更快。
对于较大的数据集,persist实际上会导致执行程序耗尽
内存(Java堆空间)。没有坚持,Spark工作就完成了。我在这里查看了其他一些建议:Spark java.lang.OutOfMemoryError: Java heap space
。
我尝试增加/减少执行程序核心,坚持
仅限磁盘,增加分区,修改存储比率,但似乎没有任何帮助执行程序内存问题。
如果有人能提到持久性是如何工作的,我会很感激,在什么情况下它比不持久更快,更重要的是,如何解决内存不足问题。
答案 0 :(得分:1)
我建议您阅读spark中transformations和actions之间的区别。我必须承认,我已经多次被自己咬过了。
火花中的数据被懒惰地评估,这基本上意味着在行动之前没有任何事情发生。执行。 .filter()
函数是一个转换,因此当代码到达该点时实际上没有任何事情发生,除了向转换管道添加一个部分。对.persist()
的调用行为方式相同。
如果您.persist()
来电的下游代码有多个可以同时触发的操作,那么很可能您实际上已经"坚持"单独的每个操作的数据,以及耗尽内存(Spark UI中的"存储'选项卡将告诉您数据集的缓存百分比,如果它超过100%缓存,那么你正在看我在这里描述的内容)。更糟糕的是,您可能永远不会真正使用缓存数据。
通常,如果您在代码中有一个点,其中数据集分为两个单独的转换管道(在您的示例中为每个或单独的.filter()
),.persist()
是一个好主意,以防止多次读取数据源,和/或在fork之前保存昂贵的转换管道的结果。
很多时候,在.persist()
调用之后(在数据分支之前)触发单个操作是个好主意,以确保后续操作(可能同时运行)从持久缓存中读取,而不是而不是独立评估(并无用地缓存)数据。
TL; DR:
在joinedDF.count()
之后,.persist()
之前.filter()
。{/ p>