写在前面:学习spark有近一个月,暂时补充RDDs算子方面的知识,每周日更新!加油!
1.RDDs:spark的主要抽象--弹性分布式数据集
RDDs的分布式可以体现在:它可被分发到集群各个节点上进行并行操作,最后将结果合并返回。
弹性体现在:spark在对RDDs操作时会自动的进行内存和磁盘数据存储的权衡和切换。
spark基于Lineage(血统关系图,下面会讲)的高效容错。
RDDs最后被分配成任务执行时,Task失败会自动进行特定次数的重试
相对于MapReduce的map和reduce两种操作而言,spark扩充了更多的方法运用在RDDs的处理上。
2.RDDs支持两种类型操作:
①actions:在数据集上运行计算后返回值,实例:
var textFile = sc.textFile("file:///usr/local/spark/README.md"),这里需要注意的是,sc.textFile()中的文件路径默认读取HDFS中的文件,若读取本地文件,需要加file://前缀。
textFile.count()//计算RDD中item的数量,对于文本文件,就是总行数。
textFile.first()//RDD中第一个item,对于文本文件,就是第一行内容
②transformations:转换,从现有数据集创建一个新的数据集,实例:
val lineWithSpark = textFile.filter(line =>line.contains("Spark"))//筛选出包含Spark的行
lineWithSpark.count()//统计行数
textFile.map(line=> line.split(" ").size).reduce((a,b)=>if(a>b) a else b)//找到包含单词最多一行内容共有几个单词。
3.文件的读取,除了上述用textFile来新建RDD,还可直接新建集合构造RDD
val rdd = sc.parallelize(Array(1,2,3,4),4)//这是创建了一个四个片区partitions的整型rdd。这里应该会想到var和val的区别。val:变量值不可修改,一旦分配,不能重新指向别的值;var:分配后可指向其他值,但是前提必须类型相同。粗俗的理解就是:val常量,var变量。
4.匿名函数及类型推断
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。例如:
val rdd =sc.textFile("file:///usr/local/1.txt")
val line=rdd.filter(line=>line.contains("hello"))//获取包含“hello”的行,即括号内的函数就是匿名函数,不需要指定参数line的类型,scala会进行类型推断,在此情境下line会被认为成rdd中的每一行,so,line只是一个参数,可替代成a,b...
5.transformations
集合运算:
①distinct()---去掉两个相同类型rdd中相同的item
②union()---两个相同类型rdd的合并,不去重
③intersection()---取两个相同类型rdd的交集
④subtract()---取两个相同类型rdd的交集的非情况
flatMap(Func)---压扁 .flatMap(line=>line.filter(" "))//将每行用空格分隔,并且组成map格式
Map(Func)---转为key-value的形式 .map(word=>(word,1))//对每一行的单词进行1计数
filter(Func)---筛选出 .filter(word=>word.contains("x"))//筛选出每行中包含x的数据
6.action
①count()//统计行数
②reduce()//对同一类型的元素做相加或其他运算 例如:.reduce((x,y)=>x+y)
③collect()//遍历整个rdd,向driver program返回rdd内容(测试使用),大数据前提下一般用saveAsTextFile()
④top()//从大到小排序并打印
⑤take()//获取rdd中几个item,无序,同时尝试访问最小的partitions
⑥foreach()//循环打印rdd中的数据,不返回至本地,不是保存操作
7.RDD特性
①血统关系图
Spark中使用DAG对RDD的关系进行建模,描述RDD的依赖关系和新建,这种关系也被称之为lineage(血统关系图)
如果在某个rdd操作中出现异常,通过血统关系图可定位到错误点,然后从上一节点重新进行一遍操作。
②延迟计算:在第一次使用actions算子的时候才真正计算。为什么呢?在处理大数据时可以减少数据传输。
同时加载数据也是延迟操作,例如:transformations操作后的rdd一直没有被利用,那么它便不会加载。
③.pesist() 持久化
对action的rdd进行缓存,不必重复之前的rdd操作。
7.keyValue RDDs
主要有以下几种,详细介绍请看图
①reduceByKey(Func)
②groupBykey()//无参
③mapValues(Func)
④flatMapValues(Func)
⑤keys()
⑥values()
⑦sortByKey()
8.combineByKey()
基于key的聚合函数,其返回类型与输入类型不一样,一共有四个参数,partitioner暂时不需要使用。
createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就
和之前的某个元素的键相同。如果这是一个新的元素, combineByKey() 会使用一个叫作 createCombiner() 的函数来创建
那个键对应的累加器的初始值
mergeValue: 如果这是一个在处理当前分区之前已经遇到的键, 它会使用 mergeValue() 方法将该键的累加器对应的当前值与这个新的值进行合并
mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更
多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。
例如 :求平均值
rdd如下:
(liqiang,80.0)//语文成绩
(liqiang,90.0) //数学成绩
(liqiang,70.0) //英语成绩
(xiaohong,70.0)//语文成绩
(xiaohong,98.0)//数学成绩
val scores = rdd.combineByKey(score=>(1,score),(c1:(Int,Double),new)=>(c1._1+1,c1._2+new)),(c1:(Int,Double),c2:(Int,Double))=>(c1._1+c2._1,c1._2+c2._2)
foreach(println)得到结果如下:
(liqiang,(3,240.0))
(xiaohong,(2,168.0))
scores.map{case(name,(num,score))=>(name,score/num)}
foreach(println)得到结果如下:
(liqiang,80.0)
(xiaohong,84.0)
9.
补充
窄依赖:指前一个rdd计算能出一个唯一的rdd,比如map或者filter等。
宽依赖:指多个rdd生成一个或者多个rdd的操作,比如groupbykey、reducebykey等