2. Spark原理-RDD及共享变量

爱学习的小朱哥

说明

  1. 本篇仅讨论运行在集群模式下的Spark。
  2. 本篇阅读时间在15min。
  3. 本篇用例均在spark-shell交互式脚本。

Spark有两个重要的概念,一个是RDD,另一个是Shard Variable。下面详细介绍。

1 RDD

RDD全称是Resilient Distributed Dataset,弹性分布式数据集。Spark运行都是建立在这一抽象的集合上,使得它可以以基本一致的方式应对不同的大数据处理场景,包括MapReduce、Streaming、SQL、Machine Learning、Graph等。

Spark支持多种编程语言,包括Python、Scala及R,RDD同样支持存储这些语言的对象。

1.1 创建RDD

Spark支持从多个地方中创建RDD,包括本地文件系统,HDFS文件系统,HBase及内存。

1.1.1 本地文件系统

//从本地文件/user/root/data.txt创建为不可变RDD
val distFile = sc.textFile("file:///user/root/data.txt")

1.1.2 HDFS文件系统

Spark默认从HDFS文件系统获取数据

val distFile = sc.textFile("/user/root/data.txt")
//指定HDFS url
val distFile = sc.textFile("hdfs://localhost:9000/user/data.txt")

1.1.3 内存

通过调用SparkContext的parallelize方法创建。

val data = Array(1, 2, 3, 4, 5)
val distData = sc.parallelize(data)

创建完RDD后我们就可以在集群对这些数据集进行并行化处理。

1.2 RDD操作

RDD支持两种类型操作:

  • 转化操作(transformation),从一个已有的RDD创建一个新的RDD;
  • 行动操作(action),对RDD进行计算出,并把结果返回到程序中,或把结果存储到外部存储系统(如HDFS)中。

转化和行动操作重要的区别是转化操作是惰性执行,只有等到行动操作开始执行时转化操作才将执行。

1.2.1 转化操作

以下RDD方法均为转化操作:

  • map
  • filter
  • flatMap
  • mapPartitions
  • sample
  • union
  • intersection
  • distinct
  • groupByKey
  • reduceByKey
  • aggregateByKey
  • sortByKey
  • join
  • cogroup
  • pipe
  • repartition

转化操作方法返回的是新的RDD。

1.2.2 行动操作

以下RDD方法均为行动操作:

  • reduce
  • reduceByKey
  • collect
  • count
  • first
  • take
  • takeSample
  • takeOrdered
  • saveAsTextFile
  • saveAsSequenceFile
  • saveAsObjectFile
  • countByKey
  • foreach

行动操作方法返回的是最终结果。

1.3 RDD缓存

Spark对RDD操作是惰性求值的,有时又希望能够多次使用同一个RDD,如果简单地对RDD调用行动操作,每次都会重新计算RDD,这在迭代算法中消耗格外大,因为迭代算法常常会多次使用同一组数据。

为了避免多次计算同一个RDD,可以让Spark对数据进行持久化。使用RDD对象的persist()方法对数据进行持久化。该方法支持传入参数,用于指定持久化位置及持久化方式。
持久化方法

val lines = sc.textFile("file:///tmp/README.md")
import org.apache.spark.storage._
lines.persist(StorageLevel.MEMORY_ONLY)

1.3.1 移除数据

Spark会自动监控缓存的使用,并基于LRU(最近最少使用)算法自动移除数据。如果你想手动移除数据,你可以使用unpersist方法。默认情况下该方法不会考虑数据是否在使用,如果你想等资源不再使用时再移除你可以指定blocking=true。

2 Shared Variable

当远程节点执行一个传入Spark操作的函数时,该函数所有的变量都会被拷贝一份到该节点,当这些变量在其他节点有更新时当前节点变量不会更新,直至返回给驱动器程序。通常意义上来说,横跨Task的读写变量是不高效的。Spark提供两种共享变量:

  • broadcast variables,广播变量
  • accumulators,累加变量

2.1 broadcast variables(广播变量)

广播变量允许开发人员在每个节点上缓存一个只读变量,无需每个任务中拷贝。广播变量可用于高效的为每个节点拷贝一份大的输入数据集。Spark同样将尝试使用高效的算法来广播变量,用以减少传输成本。

显示创建广播变量仅在Task需要横跨多个Stage且需要同一份数据或者缓存数据为逆序列化格式时才有用。

创建广播变量的方法是:

scala> val broadcastVar = sc.broadcast(Array(1, 2, 3))
broadcastVar: org.apache.spark.broadcast.Broadcast[Array[Int]] = Broadcast(0)

scala> broadcastVar.value
res0: Array[Int] = Array(1, 2, 3)

从value字段获取广播变量值。正如名字所言,广播变量是单向从驱动器到task发送的,广播变量无法更新,无法对驱动器的更新操作。保证所有节点均获取同一份数据。

调用unpersist()方法可以释放广播变量所占用的资源。如果这个资源再次被使用,该变量将重新被广播。如果要永久移除广播变量占用的资源,可以调用destory()方法。

2.2 Accumulators(累加变量)

类似于C语言中的静态变量,Spark支持数值型及用户自定义型累加变量。其特点是允许多个Task按顺序更新同一个累加变量。创建变量时可以通过SparkContext.longAccumulator()或者SparkContext.doubleAccumulator()创建long类型、double型累加器。Task可以使用add方法向累加变量添加内容。执行器程序无法读取累加变量,仅有驱动器程序才可以通过value方法读取。

scala> val accum = sc.longAccumulator("My Accumulator")
accum: org.apache.spark.util.LongAccumulator = LongAccumulator(id: 0, name: Some(My Accumulator), value: 0)

scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum.add(x))
...
10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s

scala> accum.value
res2: Long = 10

当然你也可以自定义累加变量。

引用

  1. http://spark.apache.org/docs/...
阅读 124
1 声望
0 粉丝
0 条评论
1 声望
0 粉丝
宣传栏