文章目录
1.常用的rdd
函数 | 说明 |
---|---|
map(func) | 返回一个新的分布式数据集,由每个原元素经过func函数转换后组成 |
filter(func) | 返回一个新的数据集,由经过func函数后返回值为true的原元素组成 |
flatMap(func) | 类似于map,但是每一个输入元素,会被映射为0到多个输出元素(因此,func函数的返回值是一个Seq,而不是单一元素) |
sample(withReplacement, frac, seed) | 根据给定的随机种子seed,随机抽样出数量为frac的数据 |
union(otherDataset) | 返回一个新的数据集,由原数据集和参数联合而成 |
groupByKey([numTasks]) | 在一个由(K,V)对组成的数据集上调用,返回一个(K,Seq[V])对的数据集。注意:默认情况下,使用8个并行任务进行分组,你可以传入numTask可选参数,根据数据量设置不同数目的Task |
reduceByKey(func, [numTasks]) | 在一个(K,V)对的数据集上使用,返回一个(K,V)对的数据集,key相同的值,都被使用指定的reduce函数聚合到一起。和groupbykey类似,任务的个数是可以通过第二个可选参数来配置的。 |
join(otherDataset, [numTasks]) | 在类型为(K,V)和(K,W)类型的数据集上调用,返回一个(K,(V,W))对,每个key中的所有元素都在一起的数据集 |
reduce(func) | 通过函数func聚集数据集中的所有元素。Func函数接受2个参数,返回一个值。这个函数必须是关联性的,确保可以被正确的并发执行 |
collect() | 在Driver的程序中,以数组的形式,返回数据集的所有元素。这通常会在使用filter或者其它操作后,返回一个足够小的数据子集再使用,直接将整个RDD集Collect返回,很可能会让Driver程序OOM |
count() | 返回数据集的元素个数 |
take(n) | 返回一个数组,由数据集的前n个元素组成。注意,这个操作目前并非在多个节点上,并行执行,而是Driver程序所在机器,单机计算所有的元素(Gateway的内存压力会增大) |
first() | 返回数据集的第一个元素(类似于take(1)) |
saveAsTextFile(path) | 将数据集的元素,以textfile的形式,保存到本地文件系统,hdfs或者任何其它hadoop支持的文件系统。Spark将会调用每个元素的toString方法,并将它转换为文件中的一行文本 |
saveAsSequenceFile(path) | 将数据集的元素,以sequencefile的格式,保存到指定的目录下,本地系统,hdfs或者任何其它hadoop支持的文件系统。RDD的元素必须由key-value对组成,并都实现了Hadoop的Writable接口,或隐式可以转换为Writable(Spark包括了基本类型的转换,例如Int,Double,String等等) |
foreach(func) | 在数据集的每一个元素上,运行函数func。这通常用于更新一个累加器变量,或者和外部存储系统做交互 |
rdd的输入和输出,scala版,java版
scala版本:
https://github.com/zhouyang-bigdata/SparkCommonRdd
java版本:
https://github.com/zhouyang-bigdata/SparkCommonRddJava
每个rdd函数的使用场景
spark 默认提供的rdd多达几十个,其中有与外部连接相关的,比如kafkaRdd,也有专门处理数据的。这里介绍的是专门处理数据的。
spark rdd适合的场景有:
- 转换
- 过滤
- 计数
- 排序
- 分类
- 统计
转换:
Map,最有用的转换函数。map() 接收一个函数,把这个函数用于 RDD 中的每个元素,将函数的返回结果作为结果RDD
mapPartition可以倒过来理解,先partition,再把每个partition进行map函数
map,mapToPair:一对一转换。
flatMap,flatMapToPair:一对多转换。
过滤:
filter,这通常用于规则校验,条件查询中。条件查询通常有:等于、小于、大于、小于等于、大于等于、模糊匹配,区间等。对于复杂条件,涉及到多个实体对象,需要注意序列化问题。
计数:
reduceByKey,reduce,count,accumulator
计数有2种,一种是对值本身累加,一种是对值的数量计数。
reduceByKey接收一个函数,按照相同的key进行reduce操作。
累加器也可以作为计数工具,累加器本身用处是统计程序执行次数,做一些处理,也可以做对值的数量计数。
排序:
sortByKey
用于对pairRDD按照key进行排序,第一个参数可以设置true或者false,默认是true。true是升序。
这里有个问题,就是为什么没有sort。也就是对非键值对排序,没有对应的rdd。实际上,可以通过其它rdd去实现。
分类:
groupByKey
Cogroup
Reduce
Countbykey
groupByKey会将RDD[key,value] 按照相同的key进行分组,形成RDD[key,Iterable[value]]的形式
cogroup
groupByKey是对单个 RDD 的数据进行分组,还可以使用一个叫作 cogroup() 的函数对多个共享同一个键的 RDD 进行分组 。
分类通常以某个对象为依据。按这个概念,它的用途非常广泛。
统计:
统计比较复杂,几乎没有算子单独实现需求。常见的统计有:max,min,avg,sum,趋势。
实际的例子:
- 条件查询
查询包含等于、小于、大于、小于等于、大于等于、模糊匹配,区间等条件的数据。
使用map,filter。 - 求最值
求max,min,avg,sum等数据。
具体可参见:
https://github.com/zhouyang-bigdata/SparkRddRealExamples
特殊rdd的使用选择
特殊rdd,对笔者而言,比较特别的rdd跟以下几个方面有联系:
- shuffle操作
- 在driver上还是在executor上执行
- partition的意义
- Union,join,aggregate
(1)shuffle操作
我们知道,reducebykey,groupbykey是会发生shuffle操作的。事实上,repartition,join,cogroup,和任何的By或ByKey转换可以导致洗牌。 Shuffle操作,会把转换的中间数据进行分割,然后临时存到磁盘。直到下一次转换,需要从磁盘读取数据。这个操作很低效,而且带来大量内存消耗。为什么会消耗大量内存呢?因为,对于map,filter等操作,从磁盘读取数据也好,还是处理上一步操作的中间数据,数据本身是一个抽象的rdd数据集,数据是以流的形式,小批次的放入内存,在一个时间点,内存消耗并不是很高。而这里的读磁盘数据,是一次性的读取大量数据到内存,内存消耗大。
除此之外,这2个操作,都是宽依赖,同时会产生网络io,io大小与数据量和partition的数量有关。
(2)在driver上还是在executor上执行
这是一个容易犯错的问题。比如foreachRDD,是在driver端执行。foreach,foreachPartition在executor上执行。对于面向外部连接,比如mysql连接,redis连接,hbase连接,我们不想要都在driver端创建,因为这样,每个executor端都从driver端拿数据。这里面有个情况容易混淆,就是:
数据是都在driver端处理还是仅汇总到driver端?
这个区别,直接将Action算子分为2类。
(3)partition的意义
Partition的思路,是把数据分成一块一块的,再使用task并行计算。一般推荐每个真核分2-4partition。Partition与task的关系?
可参见:
https://blog.youkuaiyun.com/cafebar123/article/details/79684596
我们知道mappartition在处理外部连接的时候,比map更有效,比如mysql,redis。
使用map与mapPartition,本质区别,是每次载入内存的数据的大小,越大,则函数执行次数越少,综合来说,执行次数越少越好,原因有二:
-
函数每执行一次,jvm就需分析一次;次数越少,jvm分析的总时间就越少。
-
对于建立外部连接时,比如读取mysql,kafka数据,建立的连接越少越高效。
但每次载入内存的数据大小,也不能过大。个人建议应不大于每个task分配的内存的1/3。