优化collect()

时间:2018-09-19 13:26:49

标签: apache-spark apache-spark-sql

我有工作代码,但是本地计算机可以在大约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行。当我查看处理的时间轴时,会得到以下信息: Timeline of exemplary test run

首先要注意的是,每个Spark任务需要1.1分钟。上面显示的代码负责图示的对collect()的前4个调用。

由于所有四个调用共享来自filter().select().group().agg()的相同数据帧,所以我认为后面的三个调用会比第一个调用快得多。显然,Spark无法识别此问题,并且每次都从原始数据帧开始。我该如何优化它,以便以后对collect()的三个调用都受益于对collect()的第一次调用的中间结果?

1 个答案:

答案 0 :(得分:2)

您关于火花每次重新执行DAG的观察是正确的,这是由于很简单的事实,即晶石是惰性的,并且火花具有两种类型的操作:

  1. 转换:选择,过滤,groupBy,orderBy,withColumn等,描述了如何转换数据框/数据集并为DAG做出贡献
  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 操作的好处。影响只有多少才能体现出来。

或者,如果您定义了要由程序回答的问题,我可以尝试帮助您编写一次即可回答的特定查询或程序。