在之前的 “5分钟入门使用Alluxio的Spark缓存” 教程里(见链接1),我们演示了如何使用Spark和Alluxio。为分享更多有关Alluxio如何增强Spark工作负载的想法和实验,本文重点介绍Alluxio如何帮助优化Spark应用程序和内存利用率。
对于不熟悉Spark的用户,在堆外内存存储数据相对直接在SparkJVM内存缓存数据的区别可能并非那么明显。为说明后一种方法的开销,这里有一个相对简单的实验:
1. 启动配置有一定内存量的本地Spark shell
2. 在执行之后步骤前,检查Spark进程的内存使用情况
3. 将大文件加载到Spark缓存中
4. 检查该Spark进程的内存使用情况以查看效果。
注意,
- 我们以一台交互式运行Spark-shell的机器开始。对于分布式系统,有时最好是从一台机器小规模开始,而非试图搞清楚大规模集群内容。
- 我们将使用驻留集大小或RSS内存大小来衡量Spark应用程序前后的主内存使用情况。
预备条件
- 安装Alluxio 2.1.0
- 安装Spark 2.4.4
- 下载示例数据并解压到/tmp
Spark缓存
我们启动一个驱动程序最大堆大小为12GB的Spark shell。在将文件加载到Spark前后,检查内存使用量。
# 启动具有配置有一定内存的Spark shell
$ bin/spark-shell --driver-memory 12g
使用uid、rss和pid检查内存大小。以下命令示例在Mac OS X上有效,但在Linux上相应命令可能有所不同。
$ ps -fo uid,rss,pid
如果不确定哪条记录与您的Spark进程相对应,请运行“jps |grep SparkSubmit”来查找该进程。
启动shell之后,执行以下命令将文件加载到Spark。
scala> val sampleRdd =sc.textFile("file:///tmp/sample-100m")scala> sampleRdd.cache()scala> sampleRdd.count()
注意,
- Spark使用惰性执行,所以sampleRdd.cache()方法告诉Spark将这些数据缓存在JVM中,但在调用操作前不会做任何事。在这种情况下,我们可以使用sampleRdd.count()操作来启动操作并将数据缓存在Spark中。
- .cache()方法是.persist(memory_only)的快捷写法,数据还可以通过.persist(MEMORY_AND_DISK)持久保存到磁盘以及内存中。
将RDD缓存到Spark JVM中后,再次检查其RSS内存大小。
$ ps -fo uid,rss,pid
在上面示例中,Spark有一个ID为78037的进程,正在使用498mb内存。
重复上述过程,但将示例数据大小分别更改为100MB、1GB、2GB和3GB。下表总结了测得的RSS内存大小差异。可以看到,Spark内部缓存数据的JVMs内存使用量上大量开销与输入数据大小成正比。
使用Alluxio作为内存中的堆外存储
在本地服务器上启动Alluxio。默认情况下,它将使用Ramdisk管理内存,内存使用量是服务器上可用内存的1/3。
$ bin/alluxio-start.sh local -f
使用配置有12GB内存的Spark shell,并指定–driver-class-path用于将Alluxio客户端jar放在classpath:
$ bin/spark-shell --driver-memory 12g \--driver-class-path${ALLUXIO_HOME}/client/alluxio-2.1.0-client.jar
现在将输入载入Spark,但将RDD保存到Alluxio
scala> val sampleRdd =sc.textFile("file:///tmp/sample-100m")scala>sampleRdd.saveAsTextFile("alluxio://localhost:19998/cache")
您可以通过列出此RDD的输出文件以及其总共大小来再次检查结果。
$ bin/alluxio fs ls /cache-rw-r--r-- binfan staff 0 PERSISTED 10-29-2019 00:12:54:438 100%/cache/_SUCCESS-rw-r--r-- binfan staff 33554451PERSISTED 10-29-2019 00:12:42:218 0% /cache/part-00000-rw-r--r-- binfan staff 33554481PERSISTED 10-29-2019 00:12:42:162 0% /cache/part-00001-rw-r--r-- binfan staff 33554372 PERSISTED 10-29-2019 00:12:42:103 0% /cache/part-00002$ bin/alluxio fs du -h -s /cacheFile Size In Alluxio Path100.79MB 100.79MB (100%) /cache
如下表所示,可以看到,将数据存储到作为堆外存储的Alluxio空间时,相比堆内方式,内存使用率要低很多。
总 结
在决定Spark如何以最佳方式利用内存时,需要考虑一些事项:
- 在任意给定的时间点,生产系统中的应用程序包含成百上千的RDDs和数据框(DataFrame)。
- 您可以增加Spark JVM的最大堆大小,但最多增加一点。我们建议最大执行程序堆大小保持在40GB左右,以减轻垃圾回收的影响。
- 应该以一定策略在Spark堆中缓存数据。
- 不同于HDFS中数据以三副本方式存储,Spark数据是通过计算产生的,如果丢失可以重计算得出。如果重计算的代价十分昂贵,那么将数据持久保存在缓存或Alluxio中是合理的。
- 尝试缓存过大的数据会导致其他数据被逐出。
牢记上述要点!Alluxio可以被看做一种优化存储方式,它以堆外内存存储的形式对Spark缓存的进行补充。如果您不确定关于你的使用场景,请随时在我们Alluxio社区的slack频道提问。参考链接:
链接1:
https://www.alluxio.io/blog/getting-started-with-alluxio-and-spark-in-5-minutes/