目录
Spark 对数据的核心抽象——弹性分布式数据集(Resilient Distributed Dataset, 简 称 RDD) 。 RDD 其实就是分布式的元素集合。在 Spark 中, 对数据的所有操作不外乎 创 建 RDD、 转化已有 RDD 以及 调用 RDD 操作进行求值。而在这一切背后, Spark 会自动将 RDD 中的数据分发到集群上,并将操作并行化执行。
一、RDD基础
Spark 中的 RDD 就是一个不可变的分布式对象集合。每个 RDD 都被分为多个 分区 ,这些 分区运行在集群中的不同节点上。RDD 可以包含 Python、Java、Scala 中任意类型的对象, 甚至可以包含用户自定义的对象。
用户可以使用两种方法创建 RDD:读取一个外部数据集, 或在驱动器程序里分发驱动器程 序中的对象集合(比如 list 和 set)
创 建 出 来 后, RDD 支 持 两 种 类 型 的 操 作:
转 化 操 作 (transformation) 和 行 动 操 作(action)
- 转化操作会由一个 RDD 生成一个新的 RDD
例如:根据谓词匹配情况筛选数 据就是一个常见的转化操作。 - 行动操作 会对 RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结 果存储到外部存储系统(如 HDFS)中
例如:first() 就是调用的一个行动操作, 它会返回 RDD 的第一个元素
转化操作和行动操作的区别:
转化操作和行动操作的区别在于 Spark 计算 RDD 的方式不同。 虽然你可以在任何时候定 义新的 RDD, 但 Spark 只会 惰性 计算这些 RDD。 它们只有第一次在一个行动操作中用到 时,才会真正计算。
原因解释:
我们以一个文本文件定义了数据, 然后把其中包 含 Python 的行筛选出来。如果 Spark 在我们运行 lines = sc.textFile(…) 时就把文件中 所有的行都读取并存储起来,就会消耗很多存储空间,而我们马上就要筛选掉其中的很多 数据。相反,一旦 Spark 了解了完整的转化操作链之后,它就可以只计算求结果时真正需 要的数据。 事实上, 在行动操作 first() 中, Spark 只需要扫描文件直到找到第一个匹配 的行为止,而不需要读取整个文件。
关于spark RDD持久化的解释:
在默认情况下,Spark 的 RDD 会在你每次对它们进行行动操作时重新计算。如果想 在多个行动操作中重用同一个 RDD, 可以使用 RDD.persist() 让 Spark 把这个 RDD 缓存 下来。在第一次对持久化的 RDD 计算之后, Spark 会把 RDD 的内容保存到内存中(以分区方式 存储到集群中的各机器上),这样在之后的行动操作中,就可以重用这些数据了。我们也 可以把 RDD 缓存到磁盘上而不是内存中。不过这对于大规模数据集是很有意义的:如果不会重用该 RDD, 我们就没有必要浪费存储空 间, Spark 可以直接遍历一遍数据然后计算出结果。
总的来说,每个 Spark 程序或 shell 会话都按如下方式工作:
- 从外部数据创建出输入 RDD。
- 使用诸如 filter() 这样的转化操作对 RDD 进行转化,以定义新的 RDD。
- 告诉 Spark 对需要被重用的中间结果 RDD 执行 persist() 操作。
- 使用行动操作(例如 count() 和 first() 等)来触发一次并行计算, Spark 会对计算进行 优化后再执行。
二、创建RDD
Spark 提供了两种创建 RDD 的方式:
1.读取外部数据集
2.在驱动器程序中对一个集合进行并行化。
创建 RDD 最简单的方式就是把程序中一个已有的集合传给 SparkContext 的 parallelize() 方法,示例如下:
scala> val lines = sc.parallelize(List("pandas", "i like pandas"))
lines: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[0] at parallelize at <console>:24
更常用的方式是从外部存储中读取数据来创建 RDD,示例如下:
scala> val rdd = sc.textFile("file:///opt/datas/stu.txt")
rdd: org.apache.spark.rdd.RDD[String] = file:///opt/datas/stu.txt MapPartitionsRDD[3] at textFile at <console>:24
三、RDD操作
RDD 支持两种操作: 转化操作 和 行动操作 。 RDD 的转化操作是返回一个新的 RDD 的操作, 比如 map() 和 filter() ,而行动操作则是向驱动器程序返回结果或 把结果写入外部系统的操作, 会触发实际的计算, 比如 count() 和 first() 。转化操作返回的是 RDD, 而行动操作返回的是其他的数据类型。
(1)转化操作
RDD 的转化操作是返回新 RDD 的操作。转化出来的 RDD 是惰性 求值的,只有在行动操作中用到这些 RDD 时才会被计算。许多转化操作都是针对各个元 素的,也就是说,这些转化操作每次只会操作 RDD 中的一个元素。不过并不是所有的转 化操作都是这样的。
举个例子,假定我们有一个日志文件 log.txt, 内含有若干消息,希望选出其中的错误消息。 我们可以使用前面说过的转化操作 filter() 。
val inputRDD = sc.textFile("log.txt")
val errorsRDD = inputRDD.filter(line => line.contains("error"))
注意