在之前的 “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 堆外内存什么时候用 spark的堆外内存_缓存

在上面示例中,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/