1.RDD编程模型
-
在 Spark 中,RDD 被表示为对象,通过对象上的方法调用来对 RDD 进行转换。 在Spark中,只有遇到action,才会执行
-
RDD 的计算(即延迟计算),这样在运行时可以通过管道的方式传输多个转换。
2.RDD的创建
在 Spark 中创建 RDD 的方式可以分为 3 种:
- 从集合中创建RDD
- 从外部存储创建RDD
- 从其他RDD转换得到新的RDD
2.1 从集合中创建RDD
-
使用parallelize函数创建
scala> val arr = Array(10,20,30,40,50,60)
arr: Array[Int] = Array(10, 20, 30, 40, 50, 60)scala> val rdd1 = sc.parallelize(arr)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at :26 -
使用makeRDD函数创建
scala> val arr2 = sc.makeRDD(Array(10,20,30,40,50,60))
arr2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at makeRDD at :24
说明:
- 一旦RDD创建成功,就可以通过并行的方式去操作这个分布式的数据集。
- parallelize和makeRDD还有一个重要的参数就是把数据集切分成分区数
- Spark会为每一个分区运行一个Task,而且会自动根据你的集群来设置分区数
2.2 从外部存储创建RDD
Spark 也可以从任意 Hadoop 支持的存储数据源来创建分布式数据集。可以是本地文件系统, HDFS, Cassandra, HVase, Amazon S3 等等。Spark 支持 文本文件, SequenceFiles, 和其他所有的 Hadoop InputFormat。
scala> var distFile = sc.textFile("words.txt")
distFile: org.apache.spark.rdd.RDD[String] = words.txt MapPartitionsRDD[1] at textFile at <console>:24
scala> distFile.collect
res0: Array[String] = Array(hello world, hello atguigu, hello spark, "hello ", scala, programming)
2.3 从其他RDD转换得到新的RDD
也就是通过RDD的各种转换算子来得到新的RDD
3. RDD的转换
在RDD上支持2种操作
- transformation,从一个已知的RDD中创建一个新的RDD
- action,在数据集上计算结束之后,给驱动程序返回一个值
在 Spark 中几乎所有的transformation操作都是懒执行的(lazy), 也就是说transformation操作并不会立即计算他们的结果, 而是记住了这个操作。 - Value类型
- Key-Value类型
3.1 Value类型1
map(func)
作用: 返回一个新的 RDD, 该 RDD 是由原 RDD 的每个元素经过函数转换后的值而组成. 就是对 RDD 中的数据做转换.
案例:创建一个包含1-10的的 RDD,然后将每个元素*2形成新的 RDD
scala> val rdd1 = sc.parallelize(1 to 10)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[2] at parallelize at <console>:24
scala> val rdd2 = rdd1.map(_ * 2)
rdd2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[3] at map at <console>:26
scala> rdd2.collect
res1: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
mapPartitionsWithIndex(func)
作用: 和mapPartitions(func)类似. 但是会给func多提供一个Int值来表示分区的索引.
所以func的类型是:(Int, Iterator) => Iterator
scala> val rdd1 = sc.parallelize(Array(10,20,30,40,50,60))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[6] at parallelize at <console>:24
scala> rdd1.mapPartitionsWithIndex((index, items) => items.map((index, _)))
res5: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[7] at mapPartitionsWithIndex at <console>:27
scala> res5.collect
res6: Array[(Int, Int)] = Array((0,10), (1,20), (1,30), (2,40), (3,50), (3,60))
flatMap(func)
作用: 类似于map,但是每一个输入元素可以被映射为 0 或多个输出元素(所以func应该返回一个序列,而不是单一元素 T =>
TraversableOnce[U])
案例:创建一个元素为 1-5 的RDD,运用 flatMap创建一个新的 RDD,新的 RDD 为原 RDD 每个元素的 平方和三次方 来组成 1,1,4,8,9,27…
scala> val rdd1 = sc.parallelize(Array(1,2,3,4,5))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[8] at parallelize at <console>:24
scala> rdd1.flatMap(x => Array(x * x, x * x * x))
res7: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[9] at flatMap at <console>:27
scala> res7.collect
res8: Array[Int] = Array(1, 1, 4, 8, 9, 27, 16, 64, 25, 125)
map()和mapPartition()的区别:
- map():每次处理一条数据。
- mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原 RDD 中该分区的数据才能释放,可能导致 OOM。
- 开发指导:当内存空间较大的时候建议使用mapPartition(),以提高处理效率。
glom()
作用: 将每一个分区的元素合并成一个数组,形成新的 RDD 类型是RDD[Array[T]]
案例:创建一个 4 个分区的 RDD&