SparkCore知识汇总

SparkCore

RDD 概述

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是 Spark 中最基本的数据抽象。代码中是一个抽象类,它代表一个弹性的不可变可分区里面的元素可并行计算的集合。

RDD 的属性
  1. 一组分区(Partition),即数据集的基本组成单位;
  2. 一个计算每个分区的函数;
  3. RDD 之间的依赖关系;
  4. 一个 Partitioner,即 RDD 的分片函数;
  5. 一个列表,存储存取每个 Partition 的优先位置(preferred location)
RDD 特点

RDD 表示只读的分区的数据集,对 RDD 进行改动,只能通过 RDD 的转换操作,由一个RDD 得到一个新的 RDD,新的 RDD 包含了从其他 RDD 衍生所必需的信息。RDDs 之间存在依赖,RDD 的执行是按照血缘关系延时计算的。如果血缘关系较长,可以通过持久化RDD 来切断血缘关系。

弹性
  • 存储的弹性:内存与磁盘的自动切换;
  • 容错的弹性:数据丢失可以自动恢复;
  • 计算的弹性:计算出错重试机制;
  • 分片的弹性:可根据需要重新分片。
分区

RDD 逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候会通过一个 compute函数得到每个分区的数据。如果 RDD 是通过已有的文件系统构建,则 compute 函数是读取指定文件系统中的数据,如果 RDD 是通过其他 RDD 转换而来,则 compute 函数是执行转换逻辑将其他 RDD 的数据进行转换

只读
  • RDD 是只读的,要想改变 RDD 中的数据,只能在现有的 RDD 基础上创建新的 RDD。

  • 由一个 RDD 转换到另一个 RDD,可以通过丰富的操作算子实现,不再像 MapReduce那样只能写 map 和 reduce 了。

  • RDD 的操作算子包括两类,一类叫做 transformations,它是用来将 RDD 进行转化,构建 RDD 的血缘关系;另一类叫做 actions,它是用来触发 RDD 的计算,得到 RDD 的相关计算结果或者将 RDD 保存的文件系统中。下图是 RDD 所支持的操作算子列表

依赖
  • RDDs 通过操作算子进行转换,转换得到的新 RDD 包含了从其他 RDDs 衍生所必需的信息,RDDs 之间维护着这种血缘关系,也称之为依赖。
    如下图所示,依赖包括两种:
    • 一种是窄依赖,RDDs 之间分区是一一对应的。
    • 另一种是宽依赖,下游 RDD 的每个分区与上游RDD(也称之为父 RDD)的每个分区都有关,是多对多的关系。
      在这里插入图片描述
缓存

一个常用的RDD可以通过cache到内存中,增加访问效率,如果在后续还有需要这个RDD的时候,会直接从缓存中取,而不会根据亲缘关系再计算一遍。

CheckPoint

RDD 支持 checkpoint 将数据保存到持久化的存储中,这样就可以切断之前的血缘关系,因为 checkpoint后的RDD 不需要知道它的父RDDs 了,它可以从 checkpoint处拿到数据。

RDD 编程

RDD的创建

三种:
从集合中创建 RDD:

  • 使用 parallelize()从集合创建
  • makeRDD()从集合创建

从外部存储创建 RDD:

  • sc.textFile(“hdfs://hadoop102:9000/RELEASE”)

从其他 RDD 创建:

  • RDD的转换操作

RDD 的转化

Value 类型
  • map(func): 输入一个,输出一个
  • mapPartitions(func): 独立运行在每个分片上的RDD的map操作
  • mapPartitionsWithIndex(func): 类似于 mapPartitions,但 func 带有一个整数参数表示分片的索引值
  • flatMap(func): 每一个输入元素可以被映射为 0 或多个输出元素,有点像hive的explode。
  • glom: 将每一个分区形成一个数组,形成新的 RDD 类型时 RDD[Array[T]]
  • groupBy(func): 分组,按照传入函数的返回值进行分组。将相同的 key 对应的值放入一个迭代器
  • filter(func): 过滤。返回一个新的 RDD,该 RDD 由经过 func 函数计算后返回值为 true 的输入元素组成。
  • sample(withReplacement, fraction, seed): 以指定的随机种子随机抽样出数量为 fraction的数据,withReplacement 表示是抽出的数据是否放回,true 为有放回的抽样,false 为无放回的抽样,seed 用于指定随机数生成器种子。
  • distinct([numTasks])): 对源 RDD 进行去重后返回一个新的 RDD。默认情况下,只有 8 个并行任务来操作,但是可以传入一个可选的 numTasks 参数改变它。
  • coalesce(numPartitions): 缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。
  • repartition(numPartitions): 根据分区数,重新通过网络随机洗牌所有数据。
  • sortBy(func,[ascending], [numTasks]): 使用 func 先对数据进行处理,按照处理后的数据比较结果排序,默认为正序。
  • pipe(command, [envVars]): 管道,针对每个分区,都执行一个 shell 脚本,返回输出的 RDD

map()和 mapPartition()的区别:

  1. map():每次处理一条数据。
  2. mapPartition():每次处理一个分区的数据,这个分区的数据处理完后,原 RDD 中分区的数据才能释放,可能导致 OOM。
  3. 开发指导:当内存空间较大的时候建议使用mapPartition(),以提高处理效率。

coalesce 和 repartition 的区别:

  1. coalesce 重新分区,可以选择是否进行 shuffle 过程。由参数 shuffle: Boolean =false/true决定。
  2. repartition 实际上是调用的 coalesce,进行 shuffle。源码如下
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
   
	 coalesce(numPartitions, shuffle = true)
	}
双 Value 类型交互
  • union(otherDataset): 对源 RDD 和参数 RDD 求并集后返回一个新的 RDD
  • subtract (otherDataset): 计算差的一种函数,去除两个 RDD 中相同的元素,不同的 RDD 将保留下来。
  • intersection(otherDataset): 对源 RDD 和参数 RDD 求交集后返回一个新的 RDD
  • cartesian(otherDataset): 笛卡尔积
  • zip(otherDataset): 将两个 RDD 组合成 Key/Value 形式的 RDD,这里默认两个 RDD 的 partition 数量以及元素数量都相同,否则会抛出异常。
Key-Value 类型
  • **partitionBy:**对 pairRDD 进行分区操作,如果原有的 partionRDD 和现有的 partionRDD 是一致的话就不进行分区, 否则会生成 ShuffleRDD,即会产生 shuffle 过程。

  • reduceByKey(func, [numTasks]): 在一个(K,V)的 RDD 上调用,返回一个(K,V)的 RDD,使用指定的 reduce 函数,将相同key 的值聚合到一起,reduce 任务的个数可以通过第二个可选的参数来设置。

  • groupByKey: groupByKey 也是对每个 key 进行操作,但只生成一个 seq

  • aggregateByKey(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U):

    • 在 kv 对的 RDD 中,按 key 将 value 进行分组合并,合并时,将每个 value 和初始值作为 seq 函数的参数,进行计算,返回的结果作为一个新的 kv 对。

    • 然后再将结果按照key 进行合并,最后将每个分组的 value 传递给 combine 函数进行计算

    • (先将前两个value进行计算,将返回结果和下一个 value 传给 combine 函数,以此类推),将 key 与计算结果作为一个新的 kv 对输出。

    • (1)zeroValue:给每一个分区中的每一个 key 一个初始值;

    • (2)seqOp:函数用于在每一个分区中用初始值逐步迭代 value;

    • (3)combOp:函数用于合并每个分区中的结果。
      在这里插入图片描述

  • foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]: aggregateByKey 的简化操作,seqop 和 combop 相同

  • combineByKey[C]: 针对相同 K,将 V 合并成一个集合。

    • 2.参数描述:
      (1)createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
      (2)mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
      (3)mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。

在这里插入图片描述

  • sortByKey([ascending], [numTasks]): 在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口,返回一个按照 key 进行排序的(K,V)的 RDD
  • mapValues: 针对于(K,V)形式的类型只对 V 进行操作
  • join(otherDataset, [numTasks]): 在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素对在一起的(K,(V,W))的 RDD。
  • cogroup(otherDataset, [numTasks]): 在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable,Iterable))类型的 RDD。

reduceByKey 和 groupByKey 的区别

  1. reduceByKey:按照 key 进行聚合,在 shuffle 之前有 combine(预聚合)操作,返回结果是 RDD[k,v]。
  2. groupByKey:按照 key 进行分组,直接进行 shuffle。
  3. 开发指导:reduceByKey 比 groupByKey,建议使用。但是需要注意是否会影响业务逻辑。

Action

  • reduce(func): 通过 func 函数聚集 RDD 中的所有元素,先聚合分区内数据,再聚合分区间数据。

  • collect(): 在驱动程序中,以数组的形式返回数据集的所有元素。

  • count(): 返回 RDD 中元素的个数。

  • first(): 返回 RDD 中的第一个元素。

  • take(n): 返回一个由 RDD 的前 n 个元素组成的数组。

  • takeOrdered(n): 返回该 RDD 排序后的前 n 个元素组成的数组。

  • aggregate:

    • 参数:(zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)
    • 作用:aggregate 函数将每个分区里面的元素通过 seqOp 和初始值进行聚合,然后用combine 函数将每个分区的结果和初始值(zeroValue)进行 combine 操作。这个函数最终返回的类型不需要和 RDD 中元素类型一致。
  • fold(num)(func): 折叠操作,aggregate 的简化操作,seqop 和 combop 一样。

  • saveAsTextFile(path): 将数据集的元素以 textfile 的形式保存到 HDFS 文件系统或者其他支持的文件系统,对于每个元素,Spark 将会调用 toString 方法,将它装换为文件中的文本

  • saveAsSequenceFile(path): 将数据集中的元素以 Hadoop sequencefile 的格式保存到指定的目录下,可以使HDFS 或者其他 Hadoop 支持的文件系统。

  • saveAsObjectFile(path): 用于将 RDD 中的元素序列化成对象,存储到文件中。

  • countByKey(): 针对(K,V)类型的 RDD,返回一个(K,Int)的 map,表示每一个 key 对应的元素个数。

  • foreach(func): 在数据集的每一个元素上,运行函数 func 进行更新。

RDD 中的函数传递

在实际开发中我们往往需要自己定义一些对于 RDD 的操作,那么此时需要主要的是,初始化工作是在 Driver 端进行的,而实际运行程序是在 Executor 端进行的,这就涉及到了跨进程通信,是需要序列化的。下面我们看几个例子:

传递一个方法:

  • 报错:: java.io.NotSerializableException: com.atguigu.Search
  • 原因:在这个方法中所调用的方法 isMatch()是定义在 Search 这个类中的,实际上调用的是this. isMatch(),this 表示 Search 这个类的对象,程序在运行过程中需要将 Search 对象序列化以后传递到 Executor 端。
  • 解决办法:使类继承 scala.Serializable 即可。

传递一个属性:

  • 报错:java.io.NotSerializableException: com.atguigu.Search
  • 原因:在这个方法中
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值