spark core实现了spark的基本功能:存储交互、任务调度、内存管理、错误恢复等;本片文章主要介绍与数据交互相关的核心技术点。

本文目录:

  • RDD特性及交互
  • shuffle操作及调优
  • RDD持久化的应用
  • Broadcast Variables&Accumulators共享变量的优势及应用场景
  • 下篇预告



spark executer 内存溢出 spark如何防止内存溢出_spark executer 内存溢出


RDD特性及交互

弹性分布式数据集(resilient distributed dataset),具备只读、可并行计算、容错性等三个主要特性。

可以由hdfs文件(包含hbase等各类以hdfs为基础的数据源)或者scala集合来创建RDD。

针对RDD,spark提供了两种类型的操作算子:transformations、action

  • transformations

主要是基于已有的RDD创建新的RDD,transformations类操作具有惰性(lazy),仅当遇见action操作需要结果时才执行,这个特性也是spark更加高效的原因之一。默认情况下,每一次action都会触发与之相关的transformations,如果一个rdd会多次使用,建议进行持久化或者缓存。


spark executer 内存溢出 spark如何防止内存溢出_spark如何防止内存溢出_02

transformations算子


  • action

是将在rdd上计算的将结果传给driver program。Job就是根据action算子划分的


spark executer 内存溢出 spark如何防止内存溢出_spark_03

action算子


spark executer 内存溢出 spark如何防止内存溢出_应用场景_04


Shuffle

指的是会引起数据的重分布的操作(re-distributing data);比如:repartition、coalesce、ByKey的操作以及join类别的操作,如:cogroup、join等。stage救赎根据shuffle算子划分的

shuffle操作需要大量的IO、网络IO以及序列化等操作,比较耗资源。

比较老的spark版本采用hash based shuffle(现在以及弃用)方式进行shuffle。举例:3个 map task, 3个 reducer, 会产生 9个小文件,


spark executer 内存溢出 spark如何防止内存溢出_spark executer 内存溢出_05

hash based shuffle


比较新的spark版本采用Consolidated HashShuffle:4个map task, 4个reducer, 如果不使用 Consolidation机制, 会产生 16个小文件。但是但是现在这 4个 map task 分两批运行在 2个core上, 这样只会产生 8个小文件。


spark executer 内存溢出 spark如何防止内存溢出_spark如何防止内存溢出_06

Consolidated HashShuffle


shuffle调优方式

  • 合理的设置partition数目
  • 防止数据倾斜
  • 合理配置一些参数,如excutor的内存,网络传输中数据的压缩方式,传输失败的重试次数等。

RDD 持久化

当一个RDD会重复使用时,可以选择持久化来缩短任务执行时间。(但如果数据量不是太大,重新生成RDD的时间消耗小于从磁盘或内存加载的时间时,就不宜持久化)

spark自动管理持久化之后的数据,会结合least-recently-used (LRU)算法删除数据。

持久化有如下几种方式:


spark executer 内存溢出 spark如何防止内存溢出_spark executer 内存溢出_07

RDD持久化


Broadcast Variables&Accumulators共享变量

  1. Broadcast Variables

针对只读变量的数据共享机制。以比较高效的方式将只读变量共享到所有机器上,当然使用普通变量也能达到类似的效果,但Broadcast类型的变量更加高效,主要具有以下优点。

  • broadcast类型变量可以保证只在executor的内存中存在一份,只需要发送一次。普通变量每个task都会发生一次数据传输。
  • 将要传输的变量不需要实现Serializable接口
  • 可以高效地传输较大的数据集

2. Accumulators

可以理解为一个只能进行加法操作的变量,spark有一些内置类型,如:longAccumulator、doubleAccumulator,用户也可自定义。Task在执行过程中只能调用其add方法,但是不能获取到他的值。只有Driver Progeam可以获取到它的值。常用的场景有计数、求和等。 示例:

val accum = sc.longAccumulator("My Accumulator")

sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum.add(x))

accum.value

下篇预告

下篇将会介绍spark core的任务调度与容错机制