我有工作代码,但是本地计算机可以在大约1分钟内完成的任务需要10分钟。因此,我认为我的代码需要优化,我认为我没有正确使用Spark,尤其是SQL limit()
和collect()
方法。
我希望/需要将问题转移到Spark(pyspark),因为我们的旧工具和计算机无法明智地处理生成的大量文件(而且显然没有资源来处理一些我们最大的文件生成)。
我正在查看CSV文件,对于每个文件(即实验),我需要首先/最后触发哪个传感器以及何时发生这些事件。
简化为我要做的Spark相关代码
tgl = dataframe.filter("<this line is relevant>") \
.select(\
substring_index(col("Name"),"Sensor <Model> <Revision> ", -1)\
.alias("Sensors"),\
col("Timestamp").astype('bigint'))\
.groupBy("Sensors").agg(min("Timestamp"),max("Timestamp"))
point_in_time = tgl.orderBy("min(Timestamp)", ascending=True).limit(2).collect()
[...]
point_in_time = tgl.orderBy("min(Timestamp)", ascending=False).limit(1).collect()
[...]
point_in_time = tgl.orderBy("max(Timetamp)", ascending=True).limit(1).collect()
[...]
point_in_time = tgl.orderBy("max(Timestamp)", ascending=False).limit(2).collect()
[...]
之所以这样做是因为我在某个地方读到,使用.limit()
通常是更明智的选择,因此并非所有数据都将被集中收集,这会花费大量时间,内存和网络容量。
我用一个文件进行测试,该文件的大小为2.5GB,长约3E7行。当我查看处理的时间轴时,会得到以下信息:
首先要注意的是,每个Spark任务需要1.1分钟。上面显示的代码负责图示的对collect()
的前4个调用。
由于所有四个调用共享来自filter().select().group().agg()
的相同数据帧,所以我认为后面的三个调用会比第一个调用快得多。显然,Spark无法识别此问题,并且每次都从原始数据帧开始。我该如何优化它,以便以后对collect()
的三个调用都受益于对collect()
的第一次调用的中间结果?
答案 0 :(得分:2)
您关于火花每次重新执行DAG的观察是正确的,这是由于很简单的事实,即晶石是惰性的,并且火花具有两种类型的操作:
数据帧不保存数据,它们是一种虚拟视图,用于描述输入数据的转换。不使用每个 collect 重新执行DAG的一种方法是缓存 tgl
tgl = dataframe.filter("<this line is relevant>") \
.select(\
substring_index(col("Name"),"Sensor <Model> <Revision> ", -1)\
.alias("Sensors"),\
col("Timestamp").astype('bigint'))\
.groupBy("Sensors").agg(min("Timestamp"),max("Timestamp"))
tgl.persist()
point_in_time = tgl.orderBy("min(Timestamp)", ascending=True).limit(2).collect()
[...]
point_in_time = tgl.orderBy("min(Timestamp)", ascending=False).limit(1).collect()
[...]
point_in_time = tgl.orderBy("max(Timetamp)", ascending=True).limit(1).collect()
[...]
point_in_time = tgl.orderBy("max(Timestamp)", ascending=False).limit(2).collect()
[...]
这将阻止DAG的重新执行,但是将 tgl 缓存到RAM会付出一定的代价,并且可能会抵消 limit 操作的好处。影响只有多少才能体现出来。
或者,如果您定义了要由程序回答的问题,我可以尝试帮助您编写一次即可回答的特定查询或程序。