zourui4271

博客园 首页 新随笔 联系 订阅 管理

Spark 与存储系统

如果 Spark 使用 HDFS 作为存储系统,则可以有效地运用 Spark 的 standalone mode cluster,让 Spark 与 HDFS 部署在同一台机器上。这种模式的部署非常简单,且读取文件的性能更高。当然,Spark 对内存的使用是有要求的,需要合理分配它与 HDFS 的资源。因此,需要配置 Spark 和 HDFS 的环境变量,为各自的任务分配内存和 CPU 资源,避免相互之间的资源争用。

若 HDFS 的机器足够好,这种部署可以优先考虑。若数据处理的执行效率要求非常高,那么还是需要采用分离的部署模式,例如部署在 Hadoop YARN 集群上。

Spark 对磁盘的要求

Spark 是 in memory 的迭代式运算平台,因此它对磁盘的要求不高。Spark 官方推荐为每个节点配置 4-8 块磁盘,且并不需要配置为 RAID(即将磁盘作为单独的 mount point)。然后,通过配置 spark.local.dir 来指定磁盘列表。

Spark 对内存的要求

Spark 虽然是 in memory 的运算平台,但从官方资料看,似乎本身对内存的要求并不是特别苛刻。官方网站只是要求内存在 8GB 之上即可(Impala 要求机器配置在 128GB)。当然,真正要高效处理,仍然是内存越大越好。若内存超过 200GB,则需要当心,因为 JVM 对超过 200GB 的内存管理存在问题,需要特别的配置。

内存容量足够大,还得真正分给了 Spark 才行。Spark 建议需要提供至少 75% 的内存空间分配给 Spark,至于其余的内存空间,则分配给操作系统与 buffer cache。这就需要部署 Spark 的机器足够干净。

考虑内存消耗问题,倘若我们要处理的数据仅仅是进行一次处理,用完即丢弃,就应该避免使用 cache 或 persist,从而降低对内存的损耗。若确实需要将数据加载到内存中,而内存又不足以加载,则可以设置 Storage Level。0.9 版本的 Spark 提供了三种 Storage Level:MEMORY_ONLY(这是默认值),MEMORY_AND_DISK,以及 DISK_ONLY。

关于数据的持久化,Spark 默认是持久化到内存中。但它也提供了三种持久化 RDD 的存储方式:

  • in-memory storage as deserialized Java objects
  • in-memory storage as serialised data
  • on-disk storage

第一种存储方式性能最优,第二种方式则对 RDD 的展现方式(Representing)提供了扩展,第三种方式则用于内存不足时。

然而,在最新版(V1.0.2)的 Spark 中,提供了更多的 Storage Level 选择。一个值得注意的选项是 OFF_HEAP,它能够将 RDD 以序列化格式存储到 Tachyon 中。相比 MEMORY_ONLY_SER,这一选项能够减少执行垃圾回收,使 Spark 的执行器(executor)更小,并能共享内存池。Tachyon 是一个基于内存的分布式文件系统,性能远超 HDFS。Tachyon 与 Spark 同源同宗,都烙有伯克利 AMPLab 的印记。目前,Tachyon 的版本为 0.5.0,还处于实验阶段。

注意,RDDs 是 Lazy 的,在执行 Transformation 操作如 map、filter 时,并不会提交 Job,只有在执行 Action 操作如 count、first 时,才会执行 Job,此时才会进行数据的加载。当然,对于一些 shuffle 操作,例如 reduceByKey,虽然仅是 Transformation 操作,但它在执行时会将一些中间数据进行持久化,而无需显式调用 persist() 函数。这是为了应对当节点出现故障时,能够避免针对大量数据进行重计算。要计算 Spark 加载的 Dataset 大小,可以通过 Spark 提供的 Web UI Monitoring 工具来帮助分析与判断。

Spark 的 RDD 是具有分区(partition)的,Spark 并非是将整个 RDD 一次性加载到内存中。Spark 针对 partition 提供了 eviction policy,这一 Policy 采用了 LRU(Least Recently Used)机制。当一个新的 RDD 分区需要计算时,如果没有合适的空间存储,就会根据 LRU 策略,将最少访问的 RDD 分区弹出,除非这个新分区与最少访问的分区属于同一个 RDD。这也在一定程度上缓和了对内存的消耗。

Spark 对内存的消耗主要分为三部分:

  1. 数据集中对象的大小;
  2. 访问这些对象的内存消耗;
  3. 垃圾回收 GC 的消耗。

一个通常的内存消耗计算方法是:内存消耗大小 = 对象字段中原生数据 * (2~5)。 这是因为 Spark 运行在 JVM 之上,操作的 Java 对象都有定义的“object header”,而数据结构(如 Map,LinkedList)对象自身也需要占用内存空间。此外,对于存储在数据结构中的基本类型,还需要装箱(Boxing)。Spark 也提供了一些内存调优机制,例如执行对象的序列化,可以释放一部分内存空间。还可以通过为 JVM 设置 flag 来标记存放的字节数(选择 4 个字节而非 8 个字节)。在 JDK 7 下,还可以做更多优化,例如对字符编码的设置。这些配置都可以在 spark-env.sh 中设置。

Spark 对网络的要求

Spark 属于网络绑定型系统,因而建议使用 10G 及以上的网络带宽。

Spark 对 CPU 的要求

Spark 可以支持一台机器扩展至数十个 CPU core,它实现的是线程之间最小共享。若内存足够大,则制约运算性能的就是网络带宽与 CPU 数。

Spark 官方利用 Amazon EC2 的环境对 Spark 进行了基准测评。例如,在交互方式下进行数据挖掘(Interative Data Mining),租用 Amazon EC2 的 100 个实例,配置为 8 核、68GB 的内存。对 1TB 的维基百科页面查阅日志(维基百科两年的数据)进行数据挖掘。在查询时,针对整个输入数据进行全扫描,只需要耗费 5-7 秒的时间。如下图所示:

在 Matei Zaharia 的 Spark 论文中还给出了一些使用 Spark 的真实案例。视频处理公司 Conviva,使用 Spark 将数据子集加载到 RDD 中。报道说明,对于 200GB 压缩过的数据进行查询和聚合操作,并运行在两台 Spark 机器上,占用内存为 96GB,执行完全部操作需要耗费 30 分钟左右的时间。同比情况下,Hadoop 需要耗费 20 小时。注意:之所以 200GB 的压缩数据只占用 96GB 内存,是因为 RDD 的处理方式,使得我们可以只加载匹配客户过滤的行和列,而非所有压缩数据。

posted on 2021-06-23 17:42  zourui4271  阅读(278)  评论(0编辑  收藏  举报