Spark学习【Spark-Core:RDD】

Spark计算框架三大数据结构:

  1. RDD:弹性分布式数据集
  2. 累加器:分布式共享只写变量
  3. 广播变量:分布式共享只读变量

RDD

是最基本的数据处理模型。它代表一个弹性的、不可变的、可分区的、内部元素可并行计算的数据集。

  • 弹性
  1. 存储:内存与磁盘的自动切换
  2. 容错:数据丢失可自动恢复
  3. 计算:计算出错的重试机制
  4. 分片:可根据需要重新分片
  • 不可变:RDD中封装了计算逻辑,不可改变。需通过产生新的RDD,在里面封装计算逻辑。
  • 可分区、并行计算
  • 数据集:RDD封装计算逻辑,不保存数据

RDD核心属性:

  1. 分区列表:用于执行任务时并行计算。
  2. 分区计算函数:对每个分区进行计算。
  3. RDD之间的依赖关系:计算模型的封装。需要组合多个计算模型时,将多个RDD建立依赖关系。
  4. 分区器(可选):当数据为KV数据集时,可设定分区器自定义数据的分区。
  5. 首选位置(可选):计算数据时,可根据计算节点的状态选择不同的节点位置进行计算。

RDD编程

val sparkConf = new SparkConf().setMaster("local[*]").setAppName("spark")
val sparkContext = new SparkContext(sparkConf)
val rdd1 = sparkContext.makeRDD(List(1,2,3,4))             //从集合创建
val rdd2 = sparkContext.textFile("/usr/local/text.txt")    //从外部存储创建
rdd1.collect().foreach(println)
rdd2.collect().foreach(println)
sparkContext.stop()

设置并行度(并行执行任务数量)

val rdd1 = sparkContext.makeRDD(List(1,2,3,4), 2)

数据分区规则Spark核心源码

    // Sequences need to be sliced at the same set of index positions for operations
    // like RDD.zip() to behave as expected
    def positions(length: Long, numSlices: Int): Iterator[(Int, Int)] = {
      (0 until numSlices).iterator.map { i =>
        val start = ((i * length) / numSlices).toInt
        val end = (((i + 1) * length) / numSlices).toInt
        (start, end)
      }
    }

RDD转换算子

  •         Value类型

        1. map

函数签名
def map[U: ClassTag](f: T => U): RDD[U]
函数说明
将待处理数据逐条进行映射转换,可以是类型或是值的转换。
val rdd: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4))
val rdd2: RDD[Int] = rdd.map(num => num * 2)
val rddString: RDD[String] = rdd.map(num => "" + num)
2,4,6,8
"1","2","3","4"

        2. mapPartitions

函数签名
def mapPartitions[U: ClassTag](
    f: Iterator[T] => Iterator[U],
    preservesPartitioning: Boolean = false): RDD[U]
函数说明
将待处理数据以分区为单位发送到计算节点进行处理,可进行数据过滤等处理操作。
val rdd: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4))
val rdd2: RDD[Int] = rdd.mapPartitions(num => num.filter(_ == 2))
2

         二者区别:

        a. 数据处理角度:map算子是对分区内数据逐个进行映射操作;mapPartitions算子以分区为单位进行批处理操作。

        b. 功能角度:map算子仅对数据值和类型进行转换,不会减少或增多数据;mapPartitions算子需传递一个迭代器,并返回一个迭代器,元素个数可以改变。

        c. 性能角度:map算子串行计算,性能较低;mapPartitions算子批处理计算,性能较高。但占用内存,可能会出现内存溢出错误。

        3. mapPartitionsWithIndex

函数签名
def mapPartitionsWithIndex[U: ClassTag](
    f: (Int, Iterator[T]) => Iterator[U],
    preservesPartitioning: Boolean = false): RDD[U]
函数说明
与mapPartitions类似,但在处理同时可获取当前分区索引。
val rdd: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4))
val rdd2: RDD[Int] = rdd.mapPartitionsWithIndex(
    (index, num) => {
        num.map(index, _)
    })

        4. flatMap

函数签名
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
函数说明
将处理的数据扁平化后在进行映射处理。也称为扁平化映射。
val rdd: RDD[List[Int]] = sparkContext.makeRDD(List(List(1, 2), List(3, 4)))
val flatRdd: RDD[Int] = rdd.flatMap(list => list)
(1,2,3,4)

        5. glom

函数签名
def glom(): RDD[Array[T]]
函数说明
将同一个分区的数据直接转换为相同类型的内存数组进行处理,且分区不变。
val rdd: RDD[List[Int]] = sparkContext.makeRDD(List(1,2,3,4),1)
val glomRDD: RDD[Array[Int]] = rdd.glom()

        6. groupBy

函数签名
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
函数说明
将数据根据指定规则进行分组,分区默认不变,但是数据会被打乱重组(shuffle)。
val rdd: RDD[List[Int]] = sparkContext.makeRDD(List(1,2,3,4))
val groupRDD: RDD[(Int, Iterable[Int])] = rdd.groupBy(_ % 2)
(0,CompactBuffer(2, 4))
(1,CompactBuffer(1, 3))

        7. filter

函数签名
def filter(f: T => Boolean): RDD[T]
函数说明
将数据根据指定规则进行过滤,符合规则的数据保留。经过滤后分区不变,但可能会出现数据倾斜情况。
val rdd: RDD[List[Int]] = sparkContext.makeRDD(List(1,2,3,4))
val filterRDD: RDD[Int] = rdd.filter(_ % 2 == 0)
2,4

        8. sample

函数签名
def sample(
    withReplacement: Boolean,
    fraction: Double,
    seed: Long = Utils.random.nextLong): RDD[T]
函数说明
将数据根据指定规则进行抽取。
val rdd: RDD[List[Int]] = sparkContext.makeRDD(List(1,2,3,4))
第一个参数表示是否放回。
第一个参数为false,则该参数只能为[0,1]。
第二个参数小于1则表示:为每个数字赋予一个概率,小于这个概率则选中。若为0则全不选,若为1则全选。若
val sampleRDD1: RDD[Int] = rdd.sample(false, 0.5)
第二个参数大于1则表示:期望每个数字被选中的次数。
val sampleRDD2: RDD[Int] = rdd.sample(true, 2)

        9. distinct

函数签名
def distinct()(implicit ord: Ordering[T] = null): RDD[T]
def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
函数说明
去重
val rdd: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4,2,4))
val disRDD: RDD[Int] = rdd.distinct()
1,2,3,4

        10. coalesce

函数签名
def coalesce(numPartitions: Int, shuffle: Boolean = false,
    partitionCoalescer: Option[PartitionCoalescer] = Option.empty)
    (implicit ord: Ordering[T] = null): RDD[T]
函数说明
根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率。
当小任务过多时,可使用coalesce聚集方法,收缩合并分区,减少任务调度成本。
val rdd: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4),4)
val coalRDD: RDD[Int] = rdd.coalesce(2)

        11. repartition

函数签名
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
函数说明
调整分区大小,内部执行coalesce操作,shuffle默认为true。
val rdd: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4),2)
val reparRDD: RDD[Int] = rdd.repartiton(4)

        12. sortBy

函数签名
def sortBy[K](
    f: (T) => K,
    ascending: Boolean = true,
    numPartitions: Int = this.partitions.length)
    (implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]
函数说明
排序操作
第一个参数为排序函数
第二个参数true为升序,false为降序
第三个参数为重新分区数
val rdd: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4,2,4),6)
val sortRDD: RDD[Int] = rdd.sortBy(num => num, false, 4)
4,4,3,2,2,1
  •         双Value类型

        1. intersection/union/subtract

函数签名
def intersection(other: RDD[T]): RDD[T]
def union(other: RDD[T]): RDD[T]
def subtract(other: RDD[T]): RDD[T]
函数说明
求两个RDD的交集/并集/差集
val rdd1: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4))
val rdd2: RDD[Int] = sparkContext.makeRDD(List(3,4,5,6))
val interRDD: RDD[Int] = rdd1.intersection(rdd2)
val unionRDD: RDD[Int] = rdd1.union(rdd2)
val subtractRDD: RDD[Int] = rdd1.subtract(rdd2)
3,4
1,2,3,4,5,6
1,2

        2. zip

函数签名
def zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]
函数说明
键值对合并。第一个RDD为key,第二个RDD为value。
val rdd1: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4))
val rdd2: RDD[Int] = sparkContext.makeRDD(List(3,4,5,6))
val zipRDD: RDD[Int] = rdd1.zip(rdd2)
(1,3)
(2,4)
(3,5)
(4,6)
  •         Key-Value类型

        1. partitionBy

函数签名
def partitionBy(partitioner: Partitioner): RDD[(K, V)]
函数说明
按照指定的partitioner重新分区。默认为HashPartitioner
val rdd1: RDD[(Int, String)] = sparkContext.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc")), 3)
val partRDD: RDD[(Int, String)] = rdd1.partitionBy(new HashPartitioner(2))
(2,bbb)

(1,aaa)
(3,ccc)

        2. reduceByKey

函数签名
def reduceByKey(func: (V, V) => V): RDD[(K, V)]
def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
函数说明
按照相同的key对value进行聚合
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4)))
val reduceRDD: RDD[(String, Int)] = rdd1.reduceByKey(_ + _)
(aaa,5)(bbb,2)(ccc,3)

        3. groupByKey

函数签名
def groupByKey(): RDD[(K, Iterable[V])]
def groupByKey(numPartitions: Int): RDD[(K, Iterable[V])]
def groupByKey(partitioner: Partitioner): RDD[(K, Iterable[V])]
函数说明
按照相同的key进行分组。若无参数,则默认为cpu核数。
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4)))
val groupRDD1: RDD[(String, Iterable[Int])] = rdd1.groupByKey()
val groupRDD2: RDD[(String, Iterable[Int])] = rdd1.groupByKey(2)
val groupRDD3: RDD[(String, Iterable[Int])] = rdd1.groupByKey(new HashPartitioner(2))
(aaa,CompactBuffer(1, 4))(bbb,CompactBuffer(2))(ccc,CompactBuffer(3))

        4. aggregateByKey

函数签名
def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,
    combOp: (U, U) => U): RDD[(K, U)]
函数说明
按照相同的key进行聚合操作。
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4)))
参数为初始值。第一个表达式为计算分区内最大值,第二个表达式为分区间求和。
val aggRDD: RDD[(String, Int)] = rdd1.aggregateByKey(0)((x, y) => math.max(x, y), (x, y) => x + y)
(aaa,5)(bbb,2)(ccc,3)

        5. foldByKey

函数签名
def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
函数说明
分区内和分区间计算规则相同时,可替换为foldByKey
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4)))
val foldRDD: RDD[(String, Int)] = rdd1.foldByKey(0)((x, y) => x + y)
(aaa,5)(bbb,2)(ccc,3)

        6. combineByKey

函数签名
def combineByKey[C](
    createCombiner: V => C,
    mergeValue: (C, V) => C,
    mergeCombiners: (C, C) => C): RDD[(K, C)]
函数说明
类似aggregateByKey但允许更改返回类型。
val list: List[(String, Int)] = List(("a", 88), ("b", 95), ("a", 91), ("b", 93),
      ("a", 95), ("b", 98))
val input: RDD[(String, Int)] = sparkContext.makeRDD(list, 2)
val combineRdd: RDD[(String, (Int, Int))] = input.combineByKey(
    (_, 1),
    (acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
    (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2)
)
(b,(286,3))
(a,(274,3))

        7. sortByKey

函数签名
def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)
 : RDD[(K, V)]
函数说明
根据key进行排序。
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4)))
val sortRDD: RDD[(String, Int)] = rdd1.sortByKey()    //true
val sortRDD: RDD[(String, Int)] = rdd1.sortByKey(false)
(aaa,1)(aaa,4)(bbb,2)(ccc,3)
(ccc,3)(bbb,2)(aaa,1)(aaa,4)

        8. join

函数签名
def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
函数说明
将两个RDD具有相同key的元素进行连接。不同元素不做处理。
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4), ("ddd", 5)))
val rdd2: RDD[(String, String)] = sparkContext.makeRDD(List(("aaa", "100"), ("bbb", "200"), ("ccc", "300"), ("aaa", "400")))
rdd1.join(rdd2)
(aaa,(1,100))
(aaa,(1,400))
(aaa,(4,100))
(aaa,(4,400))
(bbb,(2,200))
(ccc,(3,300))

        9. leftOuterJoin

函数签名
def leftOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]
函数说明
相同key进行连接操作,不同key则保留rdd1的元素,且与none连接。
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4), ("ddd", 5)))
val rdd2: RDD[(String, String)] = sparkContext.makeRDD(List(("aaa", "100"), ("bbb", "200"), ("ccc", "300"), ("aaa", "400")))
rdd1.leftOuterJoin(rdd2)
(aaa,(1,Some(100)))
(aaa,(1,Some(400)))
(aaa,(4,Some(100)))
(aaa,(4,Some(400)))
(bbb,(2,Some(200)))
(ccc,(3,Some(300)))
(ddd,(5,None))

        10. cogroup

函数签名
def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
函数说明
返回(key, rdd1中value的Iterable, rdd2中value的Iterable)
val rdd1: RDD[(String, Int)] = sparkContext.makeRDD(List(("aaa", 1), ("bbb", 2), ("ccc", 3), ("aaa", 4), ("ddd", 5)))
val rdd2: RDD[(String, String)] = sparkContext.makeRDD(List(("aaa", "100"), ("bbb", "200"), ("ccc", "300"), ("aaa", "400")))
rdd1.cogroup(rdd2)
(aaa,(CompactBuffer(1, 4),CompactBuffer(100, 400)))
(bbb,(CompactBuffer(2),CompactBuffer(200)))
(ccc,(CompactBuffer(3),CompactBuffer(300)))
(ddd,(CompactBuffer(5),CompactBuffer()))
  • RDD行动算子

        1. reduce

函数签名
def reduce(f: (T, T) => T): T
函数说明
聚集所有元素。先分区内,再分区间。
val rdd: RDD[Int] = sparkContext.makeRDD(List(1, 2, 3, 4))
val reduceRDD: Int = rdd.reduce(_ + _)
10

        2. collect

函数签名
def collect(): Array[T]
函数说明
以数组形式返回数据集所有元素
val rdd: RDD[Int] = sparkContext.makeRDD(List(1, 2, 3, 4))
rdd.collect().foreach(println)
1,2,3,4

        3. count

函数签名
def count(): Long
函数说明
返回RDD元素个数
val rdd: RDD[Int] = sparkContext.makeRDD(List(1, 2, 3, 4))
rdd.count()
4

        4. first

函数签名
def first(): T
函数说明
返回RDD第一个元素
val rdd: RDD[Int] = sparkContext.makeRDD(List(1, 2, 3, 4))
rdd.first()
1

        5. take

函数签名
def take(num: Int): Array[T]
函数说明
以数组形式返回RDD中前n个元素。
val rdd: RDD[Int] = sparkContext.makeRDD(List(1, 2, 3, 4))
val array: Array[Int] = rdd.take(3)
println(array.mkString(","))
1,2,3

        6. takeOrdered

函数签名
def takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T]
函数说明
先进行排序,然后以数组形式返回RDD中前n个元素。(默认升序排列)
val rdd: RDD[Int] = sparkContext.makeRDD(List(3, 1, 4, 2))
val array: Array[Int] = rdd.takeOrdered(3)
println(array.mkString(","))
1,2,3

若降序排列,需要传入一个Ordering实现类。实现内部的比较函数。
或直接使用top(n),返回最大n个元素
class MyOrdering extends Ordering[Int] {
  override def compare(x: Int, y: Int): Int = {
    if (x < y) 1 else if (x == y) 0 else -1
  }
}

val rdd: RDD[Int] = sparkContext.makeRDD(List(3, 1, 4, 2))
val ordering: MyOrdering = new MyOrdering()
val array: Array[Int] = rdd.takeOrdered(3)(ordering)
val array: Array[Int] = rdd.top(3)
println(array.mkString(","))
4,3,2

查看Ordering源码。调用java中Integer比较函数。

        7. aggregate

函数签名
def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U
函数说明
分区内数据与初始值进行聚合,然后分区间数据与初始值进行聚合。(分区内求和,分区间求最大)
val rdd: RDD[Int] = sparkContext.makeRDD(List(3, 1, 4, 2),4)
val result: Int = rdd.aggregate(0)(_ + _, (x, y) => math.max(x, y))
4

        8. fold

函数签名
def fold(zeroValue: T)(op: (T, T) => T): T
函数说明
分区内与分区间使用相同的聚集函数。
val rdd: RDD[Int] = sparkContext.makeRDD(List(3, 1, 4, 2),4)
val result: Int = rdd.fold(0)(_ + _)
10

        9. countByKey

函数签名
def countByKey(): Map[K, Long]
函数说明
统计key的个数
val rdd: RDD[(Int, String)] = sparkContext.makeRDD(List((1, "aaa"), (2, "bbb"), (3, "ccc"), (4, "aaa"), (1, "ddd")))
val result: collection.Map[Int, Long] = rdd.countByKey()
Map(1 -> 2, 2 -> 1, 3 -> 1, 4 -> 1)

统计value个数
val result: collection.Map[(Int, String), Long] = rdd.countByValue()
Map((3,ccc) -> 1, (1,ddd) -> 1, (4,aaa) -> 1, (1,aaa) -> 1, (2,bbb) -> 1)

        10. save

函数签名
def saveAsTextFile(path: String): Unit
def saveAsObjectFile(path: String): Unit
def saveAsSequenceFile(
    path: String,
    codec: Option[Class[_ <: CompressionCodec]] = None): Unit
函数说明
val rdd: RDD[(Int, String)] = sparkContext.makeRDD(List((1, "aaa"), (2, "bbb"), (3, "ccc"), (4, "aaa"), (1, "ddd")),3)
保存为text文件
rdd.saveAsTextFile("output")
(1,aaa)
(2,bbb)(3,ccc)
(4,aaa)(1,ddd)

保存为object文件
rdd.saveAsObjectFile("output")

保存为sequence文件
rdd.saveAsSequenceFile("output")

       

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值