基于Alluxio系统的Spark DataFrame高效存储管理技术

21次阅读

共计 3345 个字符,预计需要花费 9 分钟才能阅读完成。

介绍越来越多的公司和组织开始将 Alluxio 和 Spark 一起部署从而简化数据管理,提升数据访问性能。Qunar 最近将 Alluxio 部署在他们的生产环境中,从而将 Spark streaming 作业的平均性能提升了 15 倍,峰值甚至达到 300 倍左右。在未使用 Alluxio 之前,他们发现生产环境中的一些 Spark 作业会变慢甚至无法完成。而在采用 Alluxio 后这些作业可以很快地完成。在这篇文章中,我们将介绍如何使用 Alluxio 帮助 Spark 变得更高效,具体地,我们将展示如何使用 Alluxio 高效存储 Spark DataFrame。
Alluxio 和 Spark 缓存用户使用 Alluxio 存储 Spark DataFrame 非常简单:通过 Spark DataFrame write API 将 DataFrame 作为一个文件写入 Alluxio。通常的做法是使用 df.write.parquet() 将 DataFrame 写成 parquet 文件。在 DataFrame 对应的 parquet 文件被写入 Alluxio 后,在 Spark 中可以使用 sqlContext.read.parquet() 读取。为了分析理解使用 Alluxio 存储 DataFrame 和使用 Spark 内置缓存存储 DataFrame 在性能上差异,我们进行了如下的一些实验。
实验相关设置如下:
硬件配置:单个 worker 安装在一个节点上,节点配置:61 GB 内存 + 8 核 CPU;
软件版本:Spark 2.0.0 和 Alluxio1.2.0,参数均为缺省配置;
运行方式:以 standalone 模式运行 Spark 和 Alluxio。
在本次实验中,我们使用 Spark 内置的不同缓存级别存储 DataFrame 对比测试使用 Alluxio 存储 DataFrame,然后收集分析性能测试结果。同时通过改变 DataFrame 的大小来展示存储的 DataFrame 的规模对性能的影响。
存储 DataFrameSpark DataFrame 可以使用 persist() API 存储到 Spark 缓存中。persist() 可以缓存 DataFrame 数据到不同的存储媒介。
本次实验使用了以下 Spark 缓存存储级别(StorageLevel):
MEMORY_ONLY:在 Spark JVM 内存中存储 DataFrame 对象
MEMORY_ONLY_SER:在 Spark JVM 内存中存储序列化后的 DataFrame 对象
DISK_ONLY: 将 DataFrame 数据存储在本地磁盘
下面是一个如何使用 persist() API 缓存 DataFrame 的例子:
df.persist(MEMORY_ONLY) 将 DataFrame 保存在内存中的另一种方法是将 DataFrame 作为一个文件写入 Alluxio。Spark 支持将 DataFrame 写成多种不同的文件格式,在本次实验中,我们将 DataFrame 写成 parquet 文件。
下面是一个将 DataFrame 写入 Alluxio 的例子:
查询存储在 Alluxio 上的 DataFrameDataFrame 被保存后(无论存储在 Spark 内存还是 Alluxio 中),应用可以读取 DataFrame 以进行后续的计算任务。本次实验中,我们创建了一个包含 2 列的 DataFrame(这 2 列的数据类型均为浮点型),计算任务则是分别计算这 2 列数据之和。
当 DataFrame 存储在 Alluxio 时,Spark 读取 DataFrame 就像从 Alluxio 中读取文件一样简单。下面是一个从 Alluxio 中读取 DataFrame 的例子:
df = sqlContext.read.parquet(alluxioFile)df.agg(sum(“s1”), sum(“s2”)).show() 我们分别从 Alluxio 中 parquet 文件以及各种 Spark 存储级别缓存中读取 DataFrame,并进行上述的聚合计算操作。下图显示了不同存储方案中的聚合操作的完成时间。

从上图可以看出,从 Alluxio 中读取 DataFrame 进行聚合操作具有比较稳定的执行性能。对于从 Spark 缓存中读取 DataFrame,在 DataFrame 规模较小时执行性能具有一定优势,但是随着 DataFrame 规模的增长,性能急剧下降。在本文的实验环境中,对于各种 Spark 内置的存储级别,DataFrame 规模达到 20 GB 以后,聚合操作的性能下降比较明显。
另一方面,相比使用 Spark 内置缓存,使用 Alluxio 存储 DataFrame 并进行聚合操作,其性能在小规模数据上略有劣势。然而,随着 DataFrame 数据规模的增长,从 Alluxio 中读取 DataFrame 性能更好,因为从 Alluxio 中读取 DataFrame 的耗时几乎始终随着数据规模线性增长。由于使用 Alluxio 存储 DataFrame 的读写性能具有较好的线性可扩展性,上层应用可以稳定地以内存速度处理更大规模的数据。
使用 Alluxio 共享存储的 DataFrame 使用 Alluxio 存储 DataFrame 的另一大优势是可以在不同 Spark 应用或作业之间共享存储在 Alluxio 中的数据。当一个 DataFrame 文件被写入 Alluxio 后,它可以被不同的作业、SparkContext、甚至不同的计算框架共享。因此,如果一个存储在 Alluxio 中的 DataFrame 被多个应用频繁地访问,那么所有的应用均可以从 Alluxio 内存中直接读取数据,并不需要重新计算或者从另外的底层外部数据源中读取数据。
为了验证采用 Alluxio 共享内存的优势,我们在如上述的同样的实验环境中进行相同规模的 DataFrame 聚合操作。当使用 50 GB 规模的 DataFrame 时,我们在单个 Spark 应用中进行聚合操作,并且记录该聚合操作的耗时。没有使用 Alluxio 时,Spark 应用需要每次都从数据源读取数据 (在本次实验中是一个本地 SSD)。在使用 Alluxio 时,数据可以直接从 Alluxio 内存中读取。下图显示了 2 次聚合操作的完成时间性能对比。使用 Alluxio 的情况下,聚合操作快了约 2.5 倍。

在上图的实验中,数据源是本地 SSD。如果 DataFrame 来自访问起来更慢或不稳定的数据源,Alluxio 的优势就更加明显了。举例而言,下图是 DataFrame 数据源由本地 SSD 替换为某公有云存储的实验结果。

这张图显示是执行 7 次聚合操作的平均完成时间。图中的红色的误差范围(error bar)代表完成时间的最大和最小范围。这些结果清晰地显示出 Alluxio 可以显著提升操作的平均性能。这是因为使用 Alluxio 缓存 DataFrame 时,Spark 可以直接从 Alluxio 内存中读取 DataFrame,而不是从远程的公有云存储中。平均而言,Alluxio 可以加速上述 DataFrame 的聚集操作性能超过 10 倍。
另一方面,由于数据源是公有云系统,Spark 必须跨网络远程读取数据。错综复杂的网络状况会导致读取性能难以预测。这种性能的不稳定性从上图中的误差范围(error bar)可以很明显地看出。在不使用 Alluxio 的情况下,Spark 作业的完成时间变化范围超过 1100 秒。当使用 Alluxio 之后,完成时间的变化范围只有 10 秒。在本实验中,Alluxio 能够将数据读取造成的不稳定性降低超过 100 倍。
由于共有云存储系统的网络访问性能不可预测性,最慢的 Spark 作业执行时间超过 1700 秒, 比平均慢 2 倍。然而,当使用 Alluxio 时,最慢的 Spark 作业执行时间大约比平均时间只慢 6 秒。因此,如果以最慢的 Spark 作业执行时间来评估,Alluxio 可以加速 DataFrame 聚合操作超过 17 倍。
结论 Alluxio 可以在多个方面帮助 Spark 变得更高效。这篇文章介绍了如何使用 Alluxio 存储 Spark DataFrame,并且实验验证了采用 Alluxio 带来的优势:
Alluxio 可以直接在内存中保存大规模的数据来加速 Spark 应用;
Alluxio 能够在多个 Spark 应用之间快速共享存储在内存中的数据;
Alluxio 可以提供稳定和可预测的数据访问性能。
本文作者:开源大数据
阅读原文
本文为云栖社区原创内容,未经允许不得转载。

正文完
 0