spark-learn-02

RDD

spark是分布式计算框架,RDD是spark对分布式内存数据的抽象,可以认为RDD是spark分布式算法的数据结构;spark会将算法(RDD上的一连串操作)翻译为DAG形式的工作流进行调度,并进行task的分发;

RDD可以认为是分布式的数组,数组中的每条记录可以是用户自定义的任意数据结构;RDD是spark的基本计算单元,也是核心的数据结构,spark根据RDD的依赖关系来决定调度顺序,通过对RDD的操作形成了整个spark应用程序;

RDD创建方式

  • HDFS,Hive,Hbase…(textAsFile())
  • 父RDD转换得到子RDD
  • parallelizemakeRDD将单机数据创建为分布式RDD

算子类型

transformation

转换算子,延迟计算,一个RDD施加转换算子后,并不会立即执行,只有当遇到action算子后才会将之前的所有计算,统一计算;
转换算子用来完成作业中间过程的处理,并不会触发sparkContext提交job作业;

action

执行算子,立即计算,会触发sparkContext提交job作业,并触发计算;

RDD特性

  • 分区列表(通过分区列表可以找到一个RDD包含的所有分区及其地址,即RDD内的每个数据块所属分区及其地址)
  • 计算每个分片(即每个分区的数据)的函数(算子及自定义函数施加给RDD,实际上是算子或自定义函数会施加到RDD的每个分区)
  • 对父RDD的依赖列表(RDD之间存在依赖关系,通过依赖列表,可以回溯到父RDD,实现数据的容错)
  • 对k-v pair数据类型的RDD的分区器,可以控制分区策略和分数数量,通过分区器,可以记录数据在各个分区和节点的分配情况,防止分布不平衡
  • 每个数据分区的地址列表(如HDFS上的数据块的地址)

spark计算工作流

spark-learn-02_第1张图片

  1. spark程序运行后,数据从外部数据空间(HDFS,Hive,Hbase,或者scala集合&&数据类型)输入到spark,这时,数据就进入了spark运行时数据空间,数据会转换为spark数据块,通过BlockManager进行管理;
  2. 数据输入到spark数据空间形成RDD后,施加转换算子,形成新RDD,如果RDD需要复用,可以使用cache算子将其缓存到内存中,,然后施加action算子,触发spark提交作业,立即执行计算;
  3. spark程序运行结束,计算的结果会输出到HDFS(saveAsFlie)和Scala集合(count算子–>scala Int,collect–>scala 集合)

常用算子

map

一对一
RDD内的每个数据项----f(用户自定义函数)—>新RDD的每个数据项

spark-learn-02_第2张图片在这里插入图片描述
从图看出,map算子不改变数据所在的分区;

flatMap
map和flatMap的区别

map()是将函数用于RDD中的每个元素,将返回值构成新的RDD。

flatmap()是将函数应用于RDD中的每个元素,将返回的迭代器的所有内容构成新的RDD

map是对每一行进行处理,flatmap对每一行进行处理后 如果处理结果的长度是1,那么就和map操作一样;如果处理后是个List或者Array类型,那么就会将这个List或者Array的每个元素变成1行

scala> val rdd = sc.parallelize(List("coffee panda","happy panda","happiest panda party"))
rdd: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[0] at parallelize at <console>:24
 
scala> rdd.map(x=>x).collect
res0: Array[String] = Array(coffee panda, happy panda, happiest panda party)
 
scala> rdd.flatMap(x=>x.split(" ")).collect
res1: Array[String] = Array(coffee, panda, happy, panda, happiest, panda, party)
 
scala> val rdd1 = sc.parallelize(List(1,2,3,3))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[3] at parallelize at <console>:24
 
scala> rdd1.map(x=>x+1).collect
res2: Array[Int] = Array(2, 3, 4, 4)
 
scala> rdd1.flatMap(x=>x.to(3)).collect
res3: Array[Int] = Array(1, 2, 3, 2, 3, 3, 3)
 
从上面就可以总结出来这个规律,flaMap就是先map,然后再对每个元素flat

在实际处理中,多处理的是RDD的键值对,所以下面我举这样一个例子:


//定义一个键值对RDD,长度只有1行,已经够说明具体情况
val tmp = List(
("zhangjie",Array(("多余","11.0,25.0",1),
                  ("2511&&&&第二,长度11","第二俄日的长度11",0),
                  ("2522&&&&第二,长度22","第二俄日的长度22",0),
                  ("2533&&&&第二,长度33","第二俄日的长度33",0)))
)
val tmp1 = sc.parallelize(tmp).repartition(1)
 
// List(imei_expo(0).split(",")(0),imei_expo(0).split(",")(1))是一个List,下面分别对比下map和flatmap的区别
val tmp2 = tmp1.flatMap{t=>
  val imei_expo = t._2.filter(t=> t._3==1).map(_._2) //曝光-下载,根据逗号组成的字符串
  List(imei_expo(0).split(",")(0),imei_expo(0).split(",")(1))
}
//scala> tmp2.take(10).foreach(println)
//11.0
//25.0
 
val tmp2 = tmp1.map{t=>
  val imei_expo = t._2.filter(t=> t._3==1).map(_._2) //曝光-下载,根据逗号组成的字符串
  List(imei_expo(0).split(",")(0),imei_expo(0).split(",")(1))
}
//  scala> tmp2.count()
//  res63: Long = 1
  
  // days_keywordcuts.map(days_keywordcuts => (days_keywordcuts, imei_expo(0)))的长度是3,所以最后的tmp2的长度也是3
val tmp2 = tmp1.flatMap{t=>
  val imei_expo = t._2.filter(t=> t._3==1).map(_._2) //曝光-下载,根据逗号组成的字符串,长度为1
  val days_keywordcuts = t._2.filter(t=> t._3==0).map(_._1)       //长度为多个
  days_keywordcuts.map(days_keywordcuts => (days_keywordcuts, imei_expo(0)))
}
//scala> tmp2.take(10).foreach(println)
//(2511&&&&第二,长度11,11.0,25.0)
//(2522&&&&第二,长度22,11.0,25.0)
//(2533&&&&第二,长度33,11.0,25.0)

你可能感兴趣的:(spark-learn-02)