概述
Spark主要抽象
弹性分布式数据集(RDD)——横跨集群所有节点进行并行计算的分区元素集合;
- 用户可以要求Spark将RDD持久化到内存中,来让它在并行计算中高效地重用
- RDDs能在节点失败中自动地恢复过来
共享变量(Shared Variables)
- 两种类型的共享变量
- 广播变量——在所有节点的内存中缓存一个值;
- 累加器——仅仅能执行“添加”操作
初始化Spark
初始化Spark
Spark 编程的第一步是需要创建一个 SparkContext 对象,用来告诉 Spark 如何访问集群。在创建 SparkContext
之前,你需要构建一个 SparkConf 对象, SparkConf 对象包含了一些你应用程序的信息。
val conf = new SparkConf().setAppName(appName).setMaster(master)
new SparkContext(conf)
appName
参数是你程序的名字,它会显示在 cluster UI 上。master
是 Spark, Mesos 或 YARN 集群的 URL,或运行在本地模式时,使用专用字符串 “local”。在实践中,当应用程序运行在一个集群上时,你并不想要把 master
硬编码到你的程序中,你可以用 spark-submit 启动你的应用程序的时候传递它。然而,你可以在本地测试和单元测试中使用 “local” 运行 Spark 进程。
使用Shell
在 Spark shell 中,有一个专有的 SparkContext 已经为你创建好。在变量中叫做 sc
。你自己创建的 SparkContext 将无法工作。可以用 --master
参数来设置 SparkContext 要连接的集群,用 --jars
来设置需要添加到 classpath 中的 JAR 包,如果有多个 JAR 包使用逗号分割符连接它们。例如:在一个拥有 4 核的环境上运行 bin/spark-shell
,使用:
$ ./bin/spark-shell --master local[4]
或在 classpath 中添加 code.jar
,使用:
$ ./bin/spark-shell --master local[4] --jars code.jar
park 共享变量
共享变量
一般情况下,当一个传递给Spark操作(例如map和reduce)的函数在远程节点上面运行时,Spark操作实际上操作的是这个函数所用变量的一个独立副本。这些变量被复制到每台机器上,并且这些变量在远程机器上的所有更新都不会传递回驱动程序。通常跨任务的读写变量是低效的,但是,Spark还是为两种常见的使用模式提供了两种有限的共享变量:广播变量(broadcast variable)和累加器(accumulator)
广播变量
广播变量允许程序员缓存一个只读的变量在每台机器上面,而不是每个任务保存一份拷贝。例如,利用广播变量,我们能够以一种更有效率的方式将一个大数据量输入集合的副本分配给每个节点。(Broadcast variables allow the programmer to keep a read-only variable cached on each machine rather than shipping a copy of it with tasks.They can be used, for example,to give every node a copy of a large input dataset in an efficient manner.)Spark也尝试着利用有效的广播算法去分配广播变量,以减少通信的成本。
一个广播变量可以通过调用SparkContext.broadcast(v)
方法从一个初始变量v中创建。广播变量是v的一个包装变量,它的值可以通过value
方法访问,下面的代码说明了这个过程:
scala> val broadcastVar = sc.broadcast(Array(1, 2, 3))
broadcastVar: spark.Broadcast[Array[Int]] = spark.Broadcast(b5c40191-a864-4c7d-b9bf-d87e1a4e787c)
scala> broadcastVar.value
res0: Array[Int] = Array(1, 2, 3)
广播变量创建以后,我们就能够在集群的任何函数中使用它来代替变量v,这样我们就不需要再次传递变量v到每个节点上。另外,为了保证所有的节点得到广播变量具有相同的值,对象v不能在广播之后被修改。
此处的广播变量:broadcastVar
累加器
顾名思义,累加器是一种只能通过关联操作进行“加”操作的变量,因此它能够高效的应用于并行操作中。它们能够用来实现counters
和sums
。Spark原生支持数值类型的累加器,开发者可以自己添加支持的类型。如果创建了一个具名的累加器,它可以在spark的UI中显示。这对于理解运行阶段(running stages)的过程有很重要的作用。(注意:这在python中还不被支持)
一个累加器可以通过调用SparkContext.accumulator(v)
方法从一个初始变量v中创建。运行在集群上的任务可以通过add
方法或者使用+=
操作来给它加值。然而,它们无法读取这个值。只有驱动程序可以使用value
方法来读取累加器的值。如下的代码,展示了如何利用累加器将一个数组里面的所有元素相加:
scala> val accum = sc.accumulator(0, "My Accumulator")
accum: spark.Accumulator[Int] = 0
scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum += x)
...
10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s
scala> accum.value
res2: Int = 10
这个例子利用了内置的整数类型累加器。开发者可以利用子类AccumulatorParam创建自己的累加器类型。AccumulatorParam接口有两个方法:zero
方法为你的数据类型提供一个“0 值”(zero value);addInPlace
方法计算两个值的和。例如,假设我们有一个Vector
类代表数学上的向量,我们能够如下定义累加器:
object VectorAccumulatorParam extends AccumulatorParam[Vector] {
def zero(initialValue: Vector): Vector = {
Vector.zeros(initialValue.size)
}
def addInPlace(v1: Vector, v2: Vector): Vector = {
v1 += v2
}
}
// Then, create an Accumulator of this type:
val vecAccum = sc.accumulator(new Vector(...))(VectorAccumulatorParam)
在scala中,Spark支持用更一般的Accumulable接口来累积数据-结果类型和用于累加的元素类型不一样(例如通过收集的元素建立一个列表)。Spark也支持用SparkContext.accumulableCollection
方法累加一般的scala集合类型。