spark之算子--transformation和action

本文深入探讨了Spark中RDD的两种主要操作算子:Transformation和Action。Transformation用于创建RDD,如map、filter等,而Action触发实际计算,如reduce、collect等。文章详细解释了各种算子的功能和应用场景,特别关注它们在大数据处理中的效率和优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘要

  spark的RDD具有延迟计算的特性,spark有两种操作算子,一种是transformation,一种是action。tranformation是来创建RDD的,它有可能从文件创建,也可能集合中创建,也可能依赖其它RDD创建。当算子为transformation的时候,spark并不执行计算操作,只有当遇到action算子的时候才开始计算。这就是transformation的lazy特性。

一、transformation函数概览
1、map (func)
  --对调用map的RDD数据集中的每个element都使用func,然后返回一个新的分布式数据集RDD。

val linesRDD = sc.textfile("") //构建初始RDD
val mapRDD = linesRDD.map(x => (x,1)) // map操作

2、filter(func)
  --对调用filter的RDD数据集中的每个element都使用func,然后返回一个包含使func为true的元素构成的RDD。

val numList = Array(1,2,3,4,5)
val numRDD = sc.parallelize(numList)
val filterRDD = numRDD.filter(x => x>3)

3、flatMap(func)
  --和map算子差不多,但是flatMap生成的是多个结果。

val linesRDD = sc.textfile("") //构建初始RDD
linesRDD.flatMap(x => x.split(","))

4、mapPartitions(func)
  --和map算子差不多,但是map针对的是每个element,而mapPartitions针对的是每个partition(block)。例如,有一个数据集,有100个partiton,每个partition有10000条数据。我们要将这100w的数据保存到数据库中。如果我们用map实现,则需要链接数据库100w次,而如果用mapPartitions,则只需要链接100次。大大减少了数据库的压力。但是使用mapPartitions要考虑到内存的问题,因为它要把一个partition的数据处理完后才会释放资源,如果内存不足可能会报OOM。

5、mapPartitionsWithSplit(func)
  --和mapPartitions差不多,但是func是作用在其中的一个split上,所以func中应该有index。

6、sample(withReplacement, fraction, seed)
  --抽样

7、union(otherDataset)
  --返回一个新的dataset,包含源dataset和新给的dataset元素的集合。

8、distinct([numPartitions]))
  --返回一个新的dataset。包含源dataset里面的element经过distinct的值。

9、groupByKey([numPartitions])
  --当作用到一个(k,v)的dataset上时,返回的是一个(k,Iterable)的dataset。

10、reduceByKey(func, [numPartitions])
  --就是用一个给定的reduce func作用在groupByKey产生的(k,Iterable)的dataset之上。一般用作聚合。

注意(9,10):groupByKey和reduceByKey的区别。

 val conf = new SparkConf().setAppName("GroupAndReduce").setMaster("local")
    val sc = new SparkContext(conf)
    val words = Array("one", "two", "two", "three", "three", "three")
    val wordsRDD = sc.parallelize(words).map(word => (word, 1))
    val wordsCountWithReduce = wordsRDD.
      reduceByKey(_ + _).
      collect().
      foreach(println)
    val wordsCountWithGroup = wordsRDD.
      groupByKey().
      map(w => (w._1, w._2.sum)).
      collect().
      foreach(println)

两者都可以得到结果。但是reduceByKey更适合用在大数据集上,因为在将相同的key拉取到一个节点上进行聚合的时候,它会先做一个类似于mapreduce代码里面的combiner,先将同一个节点上的相同的key的数据进行聚合,然后在进行网络传输到最终节点。而groupByKey就没有这个动作,它会将所有相同key的数据走网络传输拉取到最终节点。这样就造成了网络与磁盘IO的压力增大。且可能造成节点OOM。

11、intersection(otherDataset)
  --返回两个dataset的一个交集dataset。

12、sortByKey([ascending], [numPartitions])
  --按照Key进行排序,ascending的值默认为True,True/False表示升序/降序 。

13、join(otherDataset, [numPartitions])
  --类似于SQL中的连接操作,即作用于键值对(K, V)和(K, W)上,返回元组 (K, (V, W))。

14、cogroup(otherDataset, [numPartitions])
  --作用于键值对(K, V)和(K, W)上,返回元组 (K, (Iterable, Iterable))。这一操作可叫做groupWith。

15、cartesian(otherDataset)
  --笛卡尔乘积,作用于数据集T和U上,返回(T, U),即数据集中每个元素的两两组合

16、coalesce(numPartitions)
  --将RDD的分区数减小到numPartitions个。当数据集通过过滤规模减小时,使用这个操作可以提升性能。一般作用在filter之后例如一个RDD有100个partition,意味着有100个task。但是经过一系列的filter过滤之后,可能每个partition里面包含的数据量已经很少了,这时再启动100个task会产生很多的小文件。这个时候就可以使用coalesce减少partition的数量。

17、repartition(numPartitions)
  --重组数据,数据被重新随机分区为numPartitions个,numPartitions可以比原来大,也可以比原来小,平衡各个分区。这一操作会将整个数据集在网络中重新洗牌。

注意(16,17):repartition算子底层调用的是coalesce,coalesce有个默认参数shuffle: Boolean = false,默认是不走shuffle操作的,repartition算子调用coalesce的时候–coalesce(numPartitions, shuffle = true)传入的是true,走shuffle,可以增大partition的数量。

二、action函数概览
1、reduce(func)
  --使用函数func(两个输入参数,返回一个值)对数据集中的元素做聚集操作

val numList = Array(1,2,3,4,5)
val numRDD = sc.parallelize(numList)
numRDD.reduce(_+_)

2、collect()
  --在driver程序中以数组形式返回数据集中所有的元素。这以action通常在执行过filter或者其他操作后返回一个较小的子数据集时非常有用。

3、count()
  --返回数据集中元素的个数。

4、first()
  --返回数据集中的第一个元素,底层调用的是take(1)

 def first(): T = withScope {
    take(1) match {
      case Array(t) => t
      case _ => throw new UnsupportedOperationException("empty collection")
    }
  }

5、take(n)
  --以数组形式返回数据集中前n个元素。需要注意的是,这一action并不是在多个node上并行执行,而是在driver程序所在的机器上单机执行,会增大内存的压力,使用需谨慎。

6、takeSample(withReplacement, num, [seed])
  --以数组形式返回从数据集中抽取的样本数量为num的随机样本,有替换或者无替换的进行采样。可选参数[seed]可以允许用户自己预定义随机数生成器的种子。注意:该方法仅在预期结果数组很小的情况下使用,因为所有数据都被加载到driver的内存中。

(1) 当不可以多次抽样:withReplacement=false;样本个数num大于父本个数时,只能返回父本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(false, 8);
System.out.println(res);

结果:[6, 8, 2, 4]

(2)当不可以多次抽样:withReplacement=false;样本个数num小于父本个数时,返回样本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(false, 3);
System.out.println(res);

结果:[8, 4, 2]

(3)当可以多次抽样:withReplacement=true;样本个数num大于父本个数时,返回样本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(true, 8);
System.out.println(res);

结果:[4, 6, 8, 2, 6, 2, 4, 2]

(4)当可以多次抽样:withReplacement=true;样本个数num小于父本个数时,返回样本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(true, 3);
System.out.println(res);

结果:[8, 8, 2]

7、takeOrdered(n, [ordering])
  --返回RDD的前n个元素,可以利用自然顺序或者由用户执行排序的comparator。

8、saveAsTextFile(path)
  --将数据集中的元素以文本文件(或者文本文件的一个集合)的形式写入本地文件系统,或者HDFS,或者其他Hadoop支持的文件系统的指定路径path下。Spark会调用每个元素的toString方法,将其转换为文本文件中的一行。

9、saveAsSequenceFile(path)
  --将数据集中的元素以Hadoop SequenceFile的形式写入本地文件系统,或者HDFS,或者其他Hadoop支持的文件系统的指定路径path下。RDD的元素必须由实现了Hadoop的Writable接口的key-value键值对组成。在Scala中,也可以是隐式可以转换为Writable的键值对(Spark包括了基本类型的转换,例如Int,Double,String等等)

10、saveAsObjectFile(path)
  --利用Java序列化,将数据集中的元素以一种简单的形式进行写操作,并能够利用SparkContext.objectFile()加载数据。(适用于Java和Scala)

11、countByKey()
  --只能作用于键值对(K, V)形式的RDDs上。按照Key进行计数,返回键值对(K, int)的哈希表。

12、foreach(func)
  --在数据集的每个元素上调用函数func。这一操作通常是为了实现一些副作用,比如更新累加器或者与外部存储系统进行交互。注意:在foreach()之外修改除了累加器以外的变量可能造成一些未定义的行为。更多内容请参阅闭包进行理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值