andyshar 2019-03-20
许多机构将Spark与Alluxio一同创建,以提高工作效率和数据可管理性。
去哪儿最近在生产中部署了Alluxio,他们的Spark流媒体工作平均加速了15倍,在高峰时段加速了300倍。一些Spark工作会减速或无法完成,但是有了Alluxio,就可以很快完成这些工作。
在这篇文章中,我们将研究Alluxio如何使Spark变得更加高效。Alluxio提高了Spark的工作效率,使Spark工作更具可预测性,并使多个Spark任务能够共享来自内存的相同数据。
Alluxio 和Spark Cache
在Alluxio内存中存储Spark 数据帧非常简单,只需要将数据帧作为文件保存到Alluxio中。用Spark 数据帧编写API将会使这个过程更简单。数据帧通常用df.write.parquet()写成parquet文件。将parquet写入Alluxio后,可以通过执行sqlContext.read.parquet()从内存中读取它。
为了理解使用Spark Cache将数据帧储存在Alluxio中的差异,我们进行了一些简单的实验。
我们选择了一个Amazon EC2 r3.2xlarge系统(内存61 GB,8核)作为实验对象,并且使用了处于默认配置状态的Spark 2.0.0和Alluxio 1.2.0。在节点上以独立模式运行Spark和Alluxio。
实验时,我们尝试了不同的方法来缓存Spark 数据帧,并将数据帧保存在Alluxio中,测量了各种方法对实验结果的影响。我们还改变了数据的大小,以得出数据大小如何影响实验结果。
保存数据帧
可以使用persist()API将Spark 数据帧“保存”或“缓存”在Spark内存中。persist()API可以将数据帧保存到不同的存储介质。为了达到实验目的,我们使用了以下Spark存储级别:
· MEMORY_ONLY:将Java对象存储在Spark JVM内存中。
· MEMORY_ONLY_SER:将序列化的Java对象存储在Spark JVM内存中。
· DISK_ONLY:将数据存储在本地磁盘上。
下面是如何使用persist()API缓存数据帧的一个示例:
df.persist(MEMORY_ONLY)
另一种将数据帧保存到内存的方法是在Alluxio中将数据帧作为文件写入。Spark支持将数据帧写为几种不同的文件格式,但在这些实验中,我们将数据帧写为parquet文件。以下是如何将数据帧写入Alluxio内存的示例:
df.write.parquet(alluxioFile)
在Alluxio中查询“已保存”的数据帧
在Spark或Alluxio中保存数据帧后,应用程序可以将其读取到计算中。在实验中,我们创建了一个带有两个浮动列的样本数据帧,计算结果是两个列的和。
当数据帧存储在Alluxio中后,在Spark中读取数据就像从Alluxio中读取文件一样简单。下面是一个从Alluxio中读取样本数据帧的示例:
df = sqlContext.read.parquet(alluxioFile)
df.agg(sum("s1"), sum("s2")).show()
我们在Alluxio parquet文件的数据帧上以及不同的Spark持久存储级别上执行了这个聚合操作,并测量了聚合所花的时间。下图显示了聚合时间。
该图显示,对从Alluxio parquet文件中读取的数据帧执行聚合操作可以极大地增加其可预测性和稳定性。然而,从Spark Cache中读取数据帧时,小数据的聚合情况较好,但较大的数据严重影响了聚合速度。对于不同的Spark存储级别,在输入大约20GB的数据之后,聚合速度会显著降低或增加。
对于较小的数据,使用Alluxio内存时数据帧聚合速度比使用Spark内存略慢,但随着数据大小的增加,使用Alluxio读取时效果明显更好,因为Alluxio会随着数据大小线性扩展。由于Alluxio读取数据时可以线性扩展,因此应用程序可以使用Alluxio以内存速度处理更大的数据。
与Alluxio共享“已保存”的数据帧
Alluxio还能够在内存中、甚至是不同的Spark任务中共享数据。将一个文件写入Alluxio后,可以通过Alluxio的内存在不同的任务、背景甚至是框架之间共享此文件。因此,如果Alluxio中的数据帧经常被许多应用程序访问,则所有应用程序都可以从内存中的Alluxio文件中读取数据,而不必重新计算或从外部源获取数据。
为了演示Alluxio内存中共享功能的优势,我们在与上述相同的环境中计算了相同的数据帧聚合时间。我们使用的数据大小为50GB,在单独的Spark应用程序中执行聚合操作,并测量执行计算所花费的时间。
在没有Alluxio的情况下,Spark应用程序必须从数据源中读取数据,在本实验中数据源指的是本地SSD。但是,当把Spark和Alluxio一起使用时,意味着能直接从Alluxio内存中读取数据。以下是聚合完成时间的实验结果图。
如果没有Alluxio,Spark必须再次从数据源读取数据(本地SSD)。从Alluxio读取数据更快,因为数据是从内存中读取的。使用Alluxio时的聚合速度是没使用Alluxio时的2.5倍多。
在上述实验中,数据源是本地SSD。但是,当数据帧的数据源较慢或不可预测时,那么Alluxio的好处就更为显著了。例如,Amazon S3是一种用来存储大量数据的当下非常流行的系统。以下是当数据帧的数据源来自Amazon S3时的实验结果。
该图显示了经过7次实验的平均聚合完成时间。图中的误差线表示完成时间的最小和最大范围。结果清楚地表明,Alluxio显著提高了计算的平均速度。这是因为使用Alluxio时,Spark可以直接从Alluxio内存中读取数据帧,而不是再次从S3获取数据。平均而言,Alluxio将数据帧的计算速度提高了10倍。
由于数据源来自Amazon S3,因此没有Alluxio的Spark必须通过网络获取数据,这需要花的时间是无法预测的。从图中的误差条可以看出这种不确定性。没有Alluxio,Spark完成工作的时间差异超过1100秒。使用Alluxio时,完成工作的时间差仅为10秒。Alluxio将不可预测性降低了100倍以上!
由于S3网络的不可预测性,没有Alluxio 时,Spark的最长运行时间可能超过1700秒,几乎是平均值的两倍。另一方面,使用Alluxio时,Spark的最慢运行速度比平均时间约长6秒。从最慢运行速度来看,Alluxio将数据帧聚合速度提高了17倍以上。