我们有时间序列数据(自1970年以来我们的时间戳和整数数据值):
# load data and cache it
df_cache = readInData() # read data from several files (paritioned by hour)
df_cache.persist(pyspark.StorageLevel.MEMORY_AND_DISK)
df_cache.agg({"data": "max"}).collect()
# now data is cached
df_cache.show()
+--------------------+---------+
| time| data|
+--------------------+---------+
|1.448409599861109E15|1551.7468|
|1.448409599871109E15|1551.7463|
|1.448409599881109E15|1551.7468|
现在我们想要使用外部python库在10分钟时间窗口之上计算一些非平凡的事情。为此,我们需要在内存中加载每个时间帧的数据,应用外部函数并存储结果。因此,无法使用用户定义的聚合函数(UDAF)。
现在的问题是,当我们将GroupBy应用于RDD时,它非常慢。
df_cache.rdd.groupBy(lambda x: int(x.time / 600e6) ). \ # create 10 minute groups
map(lambda x: 1). \ # do some calculations, e.g. external library
collect() # get results
此操作需要在两个节点上获得120Mio样本(100Hz数据),其中6GB Ram大约需要14分钟。 groupBy阶段的Spark详细信息:
Total Time Across All Tasks: 1.2 h
Locality Level Summary: Process local: 8
Input Size / Records: 1835.0 MB / 12097
Shuffle Write: 1677.6 MB / 379
Shuffle Spill (Memory): 79.4 GB
Shuffle Spill (Disk): 1930.6 MB
如果我使用一个简单的python脚本并让它迭代输入文件,那么完成的时间就会缩短。
如何在火花中优化这项工作?
答案 0 :(得分:1)
groupBy
是你的瓶颈:它需要在所有分区中对数据进行洗牌,这非常耗时并且在内存中占用了大量空间,正如您从指标中看到的那样。
这里的方法是使用reduceByKey
操作并将其链接如下:
df_cache.rdd.map(lambda x: (int(x.time/600e6), (x.time, x.data) ).reduceByKey(lambda x,y: 1).collect()
这里的关键点是groupBy
需要在所有分区中混洗所有数据,而reduceByKey
将首先在每个分区上减少,然后在所有分区上减少 - 大大减少了全球洗牌。请注意我是如何将输入组织到密钥中以利用reduceByKey
操作的。
正如我在评论中提到的那样,您可能还希望通过使用Spark SQL的DataFrame抽象来尝试您的程序,这可以通过其优化器为您提供额外的提升。