Spark编程指南-RDD编程指南

概述

Spark 应用程序由一个在集群上运行着用户的 main 函数和执行各种并行操作的 driver program(驱动程序)组成。

Spark 提供的主要抽象是一个弹性分布式数据集(RDD)RDD 可以从一个 Hadoop 文件系统或其他地方获得。了让它在整个并行操作中更高效的重用,也许会让 Spark persist(持久化)一个 RDD 到内存中。最后,RDD 会自动的从节点故障中恢复。

Spark 第二个抽象是能够用于并行操作的 shared variables(共享变量)。Spark 支持两种类型的共享变量 : broadcast variables(广播变量),它可以用于在所有节点上缓存一个值,和 accumulators(累加器),他是一个只能被 “added(增加)” 的变量,例如 counters 和 sums。


初始化 Spark

val conf = new SparkConf().setAppName(appName).setMaster(master)
new SparkContext(conf)

appName指 Spark程序的名字 自己定义
master指将程序提交到的集群路径,传local指的是在本地模式运行

使用Shell

明确使用四个核(CPU)来运行 bin/spark-shell

./bin/spark-shell --master local[4]

添加 code.jar 到它的 classpath

./bin/spark-shell --master local[4] --jars code.jar

包含一个依赖,使用 Maven 坐标

./bin/spark-shell --master local[4] --packages "org.example:example:0.1"

弹性分布式数据集 (RDDs)

Spark 主要以一个 弹性分布式数据集(RDD)的概念为中心,它是一个容错且可以执行并行操作的元素的集合。有两种方法可以创建 RDD : 在你的 driver program(驱动程序)中 parallelizing 一个已存在的集合,或者在外部存储系统中引用一个数据集,例如,一个共享文件系统,HDFS,HBase,或者提供 Hadoop InputFormat 的任何数据源。

并行集合

创建一个保存数字 1 ~ 5 的并行集合

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

并行集合中一个很重要参数是 partitions(分区)的数量,它可用来切割 dataset(数据集)。Spark 将在集群中的每一个分区上运行一个任务。通常您希望群集中的每一个 CPU 计算 2-4 个分区。一般情况下,Spark 会尝试根据您的群集情况来自动的设置的分区的数量。当然,您也可以将分区数作为第二个参数传递到 parallelize (e.g. sc.parallelize(data, 10)) 方法中来手动的设置它。

External Datasets(外部数据集)

使用SparkContext 的 textFile 方法来创建文本文件的 RDD。需要一个URL参数作为文件路径,经文件读取为行的集合(lines)

scala> val distFile = sc.textFile("data.txt")
distFile: org.apache.spark.rdd.RDD[String] = data.txt MapPartitionsRDD[10] at textFile at <console>:26

注意事项

  1. 所有 Spark 基于文件的 input 方法, 包括 textFile, 支持在目录上运行, 压缩文件, 和通配符. 例如, 您可以使用textFile("/my/directory"), textFile("/my/directory/.txt"), and textFile("/my/directory/.gz").
  2. 除了文本文件之外,Spark 的 Scala API 也支持一些其它的数据格式
RDD操作
基础
val lines = sc.textFile("data.txt")
val lineLengths = lines.map(s => s.length)
val totalLength = lineLengths.reduce((a, b) => a + b)

这里lines和lineLengths没被计算,只是一个引用,直到调用reduce
如果后面其他地方要使用lineLengths,可以使用

lineLengths.persist()
传递函数给Spark
  1. 传递匿名函数
  2. 全局单利对象的静态方法
object MyFunctions {
  def func1(s: String): String = { ... }
}

myRdd.map(MyFunctions.func1)
  1. 虽然也有可能传递一个类的实例(与单例对象相反)的方法的引用,这需要发送整个对象,包括类中其它方法
class MyClass {
  def func1(s: String): String = { ... }
  def doStuff(rdd: RDD[String]): RDD[String] = { rdd.map(func1) }
}
理解闭包
var counter = 0
var rdd = sc.parallelize(data)

// Wrong: Don't do this!!
rdd.foreach(x => counter += x)

println("Counter value: " + counter)

这段代码在同一个jvm上运行可能是对的,但是在集群中运行会导致counter为0。
因为各个执行task的机器都是引用counter的副本

为了在集群中正确执行,应该使用Accumulator

使用 Key-Value 对

大多数 Spark 操作工作在包含任何类型对象的 RDDs 上,只有少数特殊的操作可用于 Key-Value 对的 RDDs
使用的 Key-Value 对的 reduceByKey 操作统计文本文件中每一行出现了多少次

val lines = sc.textFile("data.txt")
val pairs = lines.map(s => (s, 1))
val counts = pairs.reduceByKey((a, b) => a + b)
Shuffle操作

触发的 shuffle 操作包括 repartition 操作,如 repartition 和 coalesce, ‘ByKey 操作 (除了 counting 之外) 像 groupByKey 和 reduceByKey, 和 join 操作, 像 cogroup 和 join。
shuffle 操作会大量消耗堆内存空间,存满的时候,Spark 会把溢出的数据存到磁盘上,shuffle 操作还会在磁盘上生成大量的中间文件。果 Spark 应用长期保持对 RDD 的引用,或者垃圾回收不频繁,这将导致垃圾回收的周期比较长。这意味着,长期运行 Spark 任务可能会消耗大量的磁盘空间。临时数据存储路径可以通过 SparkContext 中设置参数 spark.local.dir 进行配置。

RDD持久化

可以将RDD持久化到内存中,当下次计算时可以直接取用,具有更快的速度。
使用 persist() or cache() 方法。同时持久化时容错的,如果丢失会重新计算。同时可以设置不同的持久化等级。
Spark 会自动监视每个节点上的缓存使用情况,并使用 least-recently-used(LRU)的方式来丢弃旧数据分区。 如果您想手动删除 RDD 而不是等待它掉出缓存,使用 RDD.unpersist() 方法。


共享变量

Broadcast Variables

Broadcast variables(广播变量)将一个 read-only(只读的)变量缓存到每台机器上,而不是给任务传递一个副本。

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)

在创建广播变量之后,在集群上执行的所有的函数中,应该使用该广播变量代替原来的 v 值,所以节点上的 v 最多分发一次。另外,对象 v 在广播后不应该再被修改,以保证分发到所有的节点上的广播变量具有同样的值(例如,如果以后该变量会被运到一个新的节点)。

Accumulators(累加器)

Accumulators(累加器)只能执行add操作,比如用于在并行任务中求累计数量。
它们不能够读取它的值。只有 driver program(驱动程序)才可以使用 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

累加器只有在RDD被计算时才会增加,累加器不会改变 Spark lazy evaluation(懒加载)的模式。

val accum = sc.longAccumulator
data.map { x => accum.add(x); x }
// Here, accum is still 0 because no actions have caused the map operation to be computed.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值