spark(一)--spark-core--RDD入门实战(详解各大api,基于IDEA开发)

前言

spark系列教程

spark-core—RDD进阶知识(图文详解,基于IDEA开发)

本篇文章操作基于IDEA的本地测试,如果你还不会如何在IDEA中运行spark,请参考这篇文章

IDEA开发第一个spark程序

先说说这篇文章说了什么,

如果你对spark有所了解,应该知道spark的几大内置模块:
spark(一)--spark-core--RDD入门实战(详解各大api,基于IDEA开发)_第1张图片
这篇文章就是基于IDEA,详解spark-Core(围绕RDD展开)
即:

  • RDD创造操作
  • RDD转化操作
  • RDD行为操作

目录

RDD入门程序

RDD简介

RDD创建操作

RDD转化操作–value类型

RDD转化操作–双value类型

RDD转化操作综合案列–统计广告点击数量

RDD行为操作

0. 入门经典程序,统计字符

在项目的父目录下创建in目录,创建word.txt
看不懂?没关系,先把后面看懂了,再回来看这个

  def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //1.读取文件,将文件内容一行一行读取
    var lines:RDD[String] =  sc.textFile("in/word.txt");
    //2将一行一行数据分解成一个个单词
     var words:RDD[String]=lines.flatMap(_.split(" "));
    //3.将单词数据进行结构转化,便于统计
    var wordToOne=words.map((_,1));
    //4.两两聚合
   var sum=wordToOne.reduceByKey(_+_)
    //5打印
    var result=sum.collect();
    result.foreach(println);
  }

图解:
spark(一)--spark-core--RDD入门实战(详解各大api,基于IDEA开发)_第2张图片

1. RDD

rdd简介
通俗点来讲,可以将 RDD 理解为一个分布式对象集合,本质上是一个只读的分区记录集合。每个 RDD 可以分成多个分区,每个分区就是一个数据集片段。一个 RDD 的不同分区可以保存到集群中的不同结点上,从而可以在集群中的不同结点上进行并行计算。
spark(一)--spark-core--RDD入门实战(详解各大api,基于IDEA开发)_第3张图片

RDD 实质上是一种更为通用的迭代并行计算框架,用户可以显示控制计算的中间结果,然后将其自由运用于之后的计算。

在大数据实际应用开发中存在许多迭代算法,如机器学习、图算法等,和交互式数据挖掘工具。这些应用场景的共同之处是在不同计算阶段之间会重用中间结果,即一个阶段的输出结果会作为下一个阶段的输入。

RDD 正是为了满足这种需求而设计的。虽然 MapReduce 具有自动容错、负载平衡和可拓展性的优点,但是其最大的缺点是采用非循环式的数据流模型,使得在迭代计算时要进行大量的磁盘 I/O 操作。

通过使用 RDD,用户不必担心底层数据的分布式特性,只需要将具体的应用逻辑表达为一系列转换处理,就可以实现管道化,从而避免了中间结果的存储,大大降低了数据复制、磁盘 I/O 和数据序列化的开销。

2.RDD- 创建操作

从集合中创建
1)使用parallelize()从集合创建
val rdd = sc.parallelize(Array(1,2,3,4,5,6,7,8))
2)使用makeRDD()从集合创建
val rdd1 = sc.makeRDD(Array(1,2,3,4,5,6,7,8))

  def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //1.使用makeRDD创建
    var r1=sc.makeRDD(Array(1,2,3,4,5,6));
    r1.collect().foreach(println);

    //2.使用parallelize创建
    var r2=sc.parallelize(Array(1,2,3,4,5,6));
    r2.collect().foreach(println);
  }

从外部创建
包括本地的文件系统,还有所有Hadoop支持的数据集,比如HDFS、Cassandra、HBase等,
如前面写过的----var lines:RDD[String] = sc.textFile(“in/word.txt”);

3. 转换操作–value类型

注:下划线表示自身
若出现x._1,x._2,则x表示key-value键值对

1 map算子

  1. 作用:返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成
  2. 需求:创建一个1-10数组的RDD,将所有元素*2形成新的RDD
 def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //创建
    var arr=sc.makeRDD(1 to 10);
    var map=arr.map(_*2);
    //打印
    map.collect().foreach(println);
  }

2 mapPartitions(func) 算子

  1. 作用:类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。
  2. 需求:创建一个RDD,使每个元素*2组成新的RDD
 def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //创建
    var arr=sc.makeRDD(1 to 10);
   //
    var map=arr.mapPartitions(x=>x.map(_*2));

    map.collect().foreach(println);
  }

3 mapPartitionsWithIndex(func)

  1. 作用:类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U];
  2. 需求:创建一个RDD,使每个元素跟所在分区形成一个元组组成一个新的RDD

  def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //创建
    var arr=sc.makeRDD(1 to 5);
   //
    var map=arr.mapPartitionsWithIndex((index,item)=>(item.map((index,_))));
    map.collect().foreach(println);
  }

**4. flatMap(func) **

  1. 作用:类似于map,但是每一个输入元素可以被映射为0或多个输出元素

(所以func应该返回一个序列,而不是单一元素)

  1. 需求,创建一个二维数组,合并成一维数组

  def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //创建
   var arr=sc.makeRDD(Array(List(1 , 3),List(2 , 4)));

    var map=arr.flatMap(x=>x);

    map.collect().foreach(println);
  }

5 glom案例

  1. 作用:将每一个分区形成一个数组,形成新的RDD类型时RDD[Array[T]]
  2. 需求:创建一个4个分区的RDD,并将每个分区的数据放到一个数组
 def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //创建4个分区
    var arr=sc.makeRDD(1 to 16,4);

    var map=arr.glom();

    map.collect().foreach(arr=>{
       println(arr.mkString(","));
    });
  }

6 groupBy(func)&&filter

字面意思

    //groupby
    var arr=sc.makeRDD(1 to 10);

    var map=arr.groupBy(_%2);

    map.collect().foreach(println);

    //filter
    var arr1=sc.makeRDD(Array("xiaoming","xiaohong","xiaohuang"));
    var map1=arr1.filter(x=>(x.contains("ming")));
    map1.collect().foreach(println);

7. sample(withReplacement, fraction, seed)

  1. 作用:随机抽样

  2. 参数说明

    • withReplacement 抽样后是否放回
    • fraction 打分标准,大于fraction就抽出,小于就不抽出
    • seed 用于指定随机数生成器种子。
  3. 需求:创建一个RDD(1-10),从中选择放回和不放回抽样

    var arr=sc.makeRDD(1 to 10);

    var map=arr.sample(false,0.4,1);

    map.collect().foreach(println);

8. distinct([numTasks])) 案例

  1. 作用:对源RDD进行去重后返回一个新的RDD。默认情况下,只有8个并行任务来操作,但是可以传入一个可选的numTasks参数改变它。
  2. 需求:创建一个RDD,使用distinct()对其去重。
    var arr=sc.makeRDD(Array(1,2,2,3,4,4,5,6,6));

    var map=arr.distinct();

    map.collect().mkString(",");

9. coalesce(numPartitions) &&repartision

  1. 作用:缩减/重分配分区数,用于大数据集过滤后,提高小数据集的执行效率。
  2. 需求:创建一个4个分区的RDD,对其缩减/增分区

    var arr=sc.makeRDD(1 to 16,4);
    var map=arr.coalesce(3);
    var m=map.repartition(4);
    println(map.partitions.size);
    println(m.partitions.size);

10.sortBy

    var arr=sc.makeRDD(1 to 16);
    //按照自身大小从小到大
    var map=arr.sortBy(x=>x).collect();
    //降序
    var map1=arr.sortBy(x=>(x,false)).collect();

     //取余3后降序
    var map2=arr.sortBy(x=>(x%3,false)).collect();

4转化操作–双value类型

1. 集合

union(otherDataset)–计算并集
subtract (otherDataset) --计算差集
intersection(otherDataset)–计算交集

    var arr1=sc.makeRDD(1 to 3);
    var arr2=sc.makeRDD(2 to 4);
    var map=arr1.union(arr2);
    map.collect().foreach(println);
    println("-----")
    var map1=arr1.intersection(arr2);
    map1.collect().foreach(println);

2 cartesian(otherDataset)

  1. 作用:笛卡尔积,即两个集合元素间一 一成对

    var arr1=sc.makeRDD(1 to 3);
    var arr2=sc.makeRDD(2 to 4);
    var map=arr1.cartesian(arr2);
    map.collect().foreach(println);

结果:

(1,2)
(1,3)
(1,4)
(2,2)
(2,3)
(2,4)
(3,2)
(3,3)
(3,4)

3 . zip(otherDataset)案例

  1. 作用:将两个RDD组合成Key/Value形式的RDD,这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常。
  2. 需求:创建两个RDD,并将两个RDD组合到一起形成一个(k,v)RDD
    var arr1=sc.makeRDD(Array("a","b","c"));
    var arr2=sc.makeRDD(Array(1,2,3));
    var map=arr1.zip(arr2);
    map.collect().foreach(println);


结果:
(a,1)
(b,2)
(c,3)

5 RDD转化操作–key-value类型

1 partitionBy 案例

  1. 作用:对pairRDD进行分区操作,如果原有的partionRDD和现有的partionRDD是一致的话就不进行分区, 否则会生成ShuffleRDD,即会产生shuffle过程。
  2. 分区依据:根据传入的分区值,获取原有的hashcode,进行取模运算:
    在这里插入图片描述
  3. 需求:创建一个4个分区的RDD,对其重新分区
    var arr1=sc.makeRDD(Array((1,"a"),(2,"b"),(3,"c"),(4,"d")),4);

    var map=arr1.partitionBy(new org.apache.spark.HashPartitioner(2));

    map.collect().foreach(println);

---------------------
结果:
(2,b)
(4,d)
(1,a)
(3,c)

2 groupByKey案例

  1. 作用:根据相同的值进行分组
  2. 需求:创建一个pairRDD,将相同key对应值聚合到一个sequence中,并计算相同key对应值的相加结果。
 //1.获取k-v型rdd
    var words = Array("one", "two", "two", "three", "three", "three");
    var rdd=sc.makeRDD(words).map(x=>(x,1));

    //2.根据k进行分组

    var group=rdd.groupByKey();

    //3.将分组的值进行求和

    var result=group.map(x=>(x._1,x._2.sum));

    result.collect().foreach(println);


----result
(two,2)
(one,1)
(three,3)

3 reduceByKey(func, [numTasks]) 案例

  1. 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。
  2. 需求:创建一个pairRDD,计算相同key对应值的相加结果
    //1.获取k-v型rdd
   var rdd=sc.parallelize(List(("female",1),("male",5),("female",5),("male",2)));

    var result=rdd.reduceByKey((x,y)=>(x+y));

    result.collect().foreach(println);


------
(male,7)
(female,6)

现在反过头来看经典的统计单词案列,你是否看懂了?

```java
  def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //1.读取文件,将文件内容一行一行读取
    var lines:RDD[String] =  sc.textFile("in/word.txt");
    //2将一行一行数据分解成一个个单词
     var words:RDD[String]=lines.flatMap(_.split(" "));
    //3.将单词数据进行结构转化,便于统计
    var wordToOne=words.map((_,1));
    //4.两两聚合
   var sum=wordToOne.reduceByKey(_+_)
    //5打印
    var result=sum.collect();
    result.foreach(println);
  }

4 reduceByKey和groupByKey的区别

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

5 aggregateByKey案例

参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U)

  1. 作用:在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
  2. 参数描述:
    (1)zeroValue:给每一个分区中的每一个key一个初始值;
    (2)seqOp:函数用于在每一个分区中用初始值逐步迭代value;
    (3)combOp:函数用于合并每个分区中的结果。
  3. 需求:创建一个pairRDD,取出每个分区相同key对应值的最大值,然后相加
  4. 需求分析
    spark(一)--spark-core--RDD入门实战(详解各大api,基于IDEA开发)_第4张图片

    //1.获取k-v型rdd
    val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
    //取出每个分区相同key对应值的最大值,然后相加
    val result=rdd.aggregateByKey(0)(math.max(_,_),_+_);

    result.collect().foreach(println);

------
(b,3)
(a,3)
(c,12)


6. foldByKey案例
参数:(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
1.作用:和reduceByKey类似

7 sortByKey([ascending], [numTasks])

  1. 作用:在一个(K,V)的RDD上调用,返回一个按照key进行排序的(K,V)的RDD
  2. ascending为bool,决定升降序

8 mapValues案例

  1. 针对于(K,V)形式的类型只对V进行操作
  2. 需求:创建一个pairRDD,并将value添加字符串"|||"

    val rdd3 = sc.parallelize(Array((1,"a"),(1,"d"),(2,"b"),(3,"c")));
    rdd3.mapValues((value)=>(value+"|||||")).collect().foreach(println);

9 join(otherDataset, [numTasks]) 案例

  1. 作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD
  2. 需求:创建两个pairRDD,并将key相同的数据聚合到一个元组。
    val rdd = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")));

    val rdd1=sc.parallelize(Array((1,4),(2,5),(3,6)));

    rdd.join(rdd1).collect().foreach(println);

----------------------
(1,(a,4))
(3,(c,6))
(2,(b,5))

10 cogroup(otherDataset, [numTasks]) 案例

  1. 作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD
  2. 需求:创建两个pairRDD,并将key相同的数据聚合到一个迭代器。
    val rdd = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")));

    val rdd1=sc.parallelize(Array((1,4),(2,5),(3,6)));

    rdd.cogroup(rdd1).collect().foreach(println);


-------
(1,(CompactBuffer(a),CompactBuffer(4)))
(3,(CompactBuffer(c),CompactBuffer(6)))
(2,(CompactBuffer(b),CompactBuffer(5)))

6. RDD转化操作综合案列–统计广告点击数量

  1. 数据结构:省份,城市,广告, 中间字段使用空格分割。

样本如下:

asdf aaa 4
asdf aaa 3
asdf aaa 4
asdf aaa 2
asdf aaa 1
qwer asdf 1
qwer asdf 2
qwer asdf 3
qwer asdf 1
qwer asdf 2

  1. 需求:统计出每一个省份广告被点击次数的TOP3
  2. 思路:
  • 读取文件
  • 按照最小粒度聚合:((Province,AD),1)
  • 计算每个省中每个广告被点击的总数:((Province,AD),sum)
  • 将省份作为key,广告加点击数为value:(Province,(AD,sum))
  • 将同一个省份的所有广告进行聚合(Province,List((AD1,sum1),(AD2,sum2)…))
  • 对同一个省份所有广告的集合进行排序并取前3条,排序规则为广告点击总数
 def main(args: Array[String]): Unit = {
    //上下文
    var conf=new SparkConf().setAppName("WordCount").setMaster("local");
    var sc=new SparkContext(conf);
    sc.setLogLevel("ERROR")

    //读文件
    val rdd=sc.textFile("in/word.txt");

    //转化成((province,AD),1)

    val r1=rdd.map(x=>{
      val line=x.split(" ");
      ((line(0),line(2)),1);
    })

    //计算每个省每个广告被点击总数

    var r2=r1.reduceByKey(_+_);

    //转化成 (province,(AD,times))

    val r3=r2.map(x=>(x._1._1,(x._1._2,x._2)));

    //分组
    var r4=r3.groupByKey();

    //计算前三名的广告

    val r5=r4.mapValues(xml=>{
      xml.toList.sortWith((x,y)=>{
         x._2>y._2
      }).take(3);
    })

    //打印
    r5.collect().foreach(println);

   
  }

加油!!快结束了

7. RDD行为操作

1 基本

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

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

  • first ()–返回第一个元素

  • take(n)–返回前n个元素

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

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

1 reduce(func)案例

  1. 作用:通过func函数聚集RDD中的所有元素,先聚合分区内数据,再聚合分区间数据。
  2. 需求:创建一个RDD,将所有元素聚合得到结果。

    val rdd2 = sc.makeRDD(Array(("a",1),("a",3),("c",3),("d",5)));
    val map=rdd2.reduce((x,y)=>{
      (x._1+y._1,x._2+y._2);
    });
    println(map);

-------
(aacd,12)


3 . aggregate案例

  1. 参数:(zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)
  2. 作用:aggregate函数将每个分区里面的元素通过seqOp和初始值进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。这个函数最终返回的类型不需要和RDD中元素类型一致。
  3. 需求:创建一个RDD,将所有元素相加得到结果
   val rdd=sc.makeRDD(1 to 10,2);

    var r=rdd.aggregate(0)(_+_,_+_);
    
    println(r);

----
55

fold(num)(func)–折叠操作,aggregate的简化操作,seqop和combop一样。
countByKey()案例

文件操作

  • saveAsTextFile(path)
    作用:将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本
  • saveAsSequenceFile(path)
    作用:将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。
  • saveAsObjectFile(path)
    作用:用于将RDD中的元素序列化成对象,存储到文件中。

恭喜你,终于读完了这个又臭又长的教程!!!

下一篇文章–RDD进阶知识

你可能感兴趣的:(spark)