spark RDD算子总结

在spark数据处理中,一些算子使用频道很高。为此,总结在工作中常用的一些算子,并结合数据进行说明。

一、 二次排序

二次排序属于日志处理中,经常遇到,而使用scala可以极大简化代码。数据secondarysortExample.txt数据如下;

2,2

1,31

4,4

1,11

2,2

1,31

4,4

1,1

3,8

class SecondarySort(val first:Int, val second:Int) extends Ordered[SecondarySort] with Serializable{

override def compare(that: SecondarySort) :Int = {

if(this.first - that.first != 0){

this.first -that.first

}else{

this.second - that.second

}

}

}

val rd1 = sc.textFile("/opt/data/secondarysortExample.txt")

val rd2 = rd1.map(line=>(line.split(",")(0).toInt,line.split(",")(1).toInt))

val rd3 = rd2.map(f=>(new SecondarySort(f._1,f._2),f))

val rd4 = rd3.sortByKey(false)

二、 spark transform操作

1.map mapPartition flatMap mapValues mapPartitionsWithIndex算子

map输入rdd中每个元素,mapPartition输入是rdd每个分区数据

map一对一,flatMap一对多

mapValues KV键值对rdd,key不变

mapPartitionsWithIndex func函数(Int,Iterator)=>Iterator

示例:

git.txt数据

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

Git is easy to learn and has a tiny footprint with lightning fast performance. It outclasses SCM tools like Subversion, CVS, Perforce, and ClearCase with features like cheap local branching, convenient staging areas, and multiple workflows.

# 读取git.txt数据

val rdd1 = sc.textFile("/opt/data/git.txt",2)

rdd1.collect()

# 一对多

val rdd2 = rdd1.flatMap(line=>line.split(" "))

rdd2.collect()

# 一对一,转换为KV pairs

val rdd3 = rdd2.map(f=>(f,1))

rdd3.collect()

# KV pairs value转换

val rdd4 = rdd3.mapValues(f=>2*f)

rdd4.collect()

# 统计键值对中个元素分区

def mapPartIndexFunc(i1:Int,iter:Iterator[(String,Int)]):Iterator[(Int,(String,Int))]={

| var res = List[(Int,(String,Int))]()

| while(iter.hasNext){

| var next = iter.next()

| res=res.::(i1,next)

| }

| res.iterator

| }

val rdd5 = rdd4.mapPartitionsWithIndex(mapPartIndexFunc)

rdd5.collect()

2. filter

val rdd6 = rdd2.filter(f=>f.length > 5)

rdd6.collect()

3. sample

sample(withReplacement,fraction,seed)是根据给定随机种子seed,随机抽样出数量为frac的数据。withReplacement:是否放回抽样

val rdd7 = rdd2.sample(false, 0.1, 2)

rdd7.collect()

4. union

两个集合数据类型一致

val rdd8 = rdd6.union(rdd7)

rdd8.collect()

5. intersection

返回两个集合的交集

val rdd9 = rdd8.intersection(rdd6)

rdd9.collect()

6. distinct

返回两个集合去重后新集合

val rdd10 = rdd9.union(rdd9).distinct()

7. groupByKey reduceByKey aggregateByKey

groupByKey([numTasks])是对KV键值对数据分组操作,返回(K,Seq[V]的数据集,通过numTasks设置不同并行任务;

reducByKey(func,[numTasks])是对KV键值对数据分组聚合操作,返回(K,V)的数据集;

区别在于:数据两大,reduceByKey性能更佳,因为在shuffle输出数据前,会combine每个partition上具有相同key的输出结果,而groupByKey所有键值对都会shuffle。

aggregateByKey

aggregateByKey(zeroValue:U)(seqOp:(U,T)=>U,combOp:(U,U)=>U)

seqOp操作会聚合各分区中的元素,然后combOp操作把所有分区的聚合结果再次聚合,两个操作的初始值都是zeroValue;seqOp的操作是遍历分区中的所有元素(T),第一个T跟zeroValue做操作,结果再作为与第二个T做操作的zeroValue,直到遍历完整个分区。

val rdd11 = rdd5.groupByKey()

rdd11.collect()

val rdd12 = rdd3.reduceByKey(_+_,2)

rdd12.collect()

# 查看rdd3各分区数据

val rdd13 = rdd2.map(f=>(f,f.length))

rdd13.collect()

val rdd14 = rdd13.mapPartitionsWithIndex{

(partIdx,iter)=>{

var part_map = scala.collection.mutable.Map[String,List[(String,Int)]]()

while(iter.hasNext){

var part_name = "part_" + partIdx

var elem = iter.next

if(part_map.contains(part_name)){

var elems = part_map(part_name)

elems ::=elem

part_map(part_name) = elems

}else{

part_map(part_name) = List[(String,Int)]{elem}

}

}

part_map.iterator

}

}

rdd14.collect()

val rdd15 = rdd14.aggregateByKey(0)(min(_,_),_+_)

rdd15.collect()

8. sortByKey

sortByKey([ascending],[numTasks])对KV pairs数据按照K进行排序,第一个参数ascending,默认,升序,第二个参数,并行任务数量。

val rdd16 = rdd3.sortByKey()

rdd16.collect()

sortByKey通过重写ordering修改默认排序规则

implicit val sortByLength = new Ordering[String]{

override def compare(a:String, b:String) =

a.length.compare(b.length)

}

val rdd17 = rdd3.sortByKey()

rdd17.collect()

9. join leftOuterJoin RightOuterJoin fullOuterJoin

val rdd18 = rdd3.reduceByKey(_+_)

val rdd19 = rdd18.sample(false,0.1,2)

rdd18.collect()

rdd19.collect()

val rdd20 = rdd18.join(rdd19)

rdd20.collect()

val rdd21 = rdd18.leftOuterJoin(rdd19)

rdd21.collect()

val rdd22 = rdd19.rightOuterJoin(rdd18)

rdd22.collect()

10. cogroup cartesian

cogroup(otherDataset,[numTasks])是将输入数据集(K,V)和另外一个数据集(K,W)聚合一个集合(K,Seq[V],Seq[W]);

cartesian(otherDataset)是对数据集T和U做笛卡尔积操作

val rdd23 = rdd19.cogroup(rdd19)

rdd23.collect()

val rdd24 = rdd19.cartesian(rdd19)

rdd24.collect()

11. pipe

pipe(command,[envVars])是以shell命令处理rdd

rdd24.pipe("head -n 1").collect()

12. randomSplit

randomSplit(weights:Array[Double],seed:Long = Utils.random.nextLong):Array[RDD[T]]

对RDD按照权重进行数据分割,第一个参数为分割权重数组,第二个参数为随机种子。

val rdds = rdd18.randomSplit(Array(0.3,0.7),2)

val rdd25 = rdds(0)

val rdd26 = rdds(1)

rdd25.collect()

rdd26.collect()

13. subtract

subtract(other:RDD[T]):RDD[T]是对RDD进行减法操作

val rdd27 = rdd18.subtract(rdd25)

rdd27.collect()

14. zip zipWithIndex zipPartitions

zip[U:ClassTag](other:RDD[U]):RDD[(T,U)]将两个RDD组合成KV pairs RDD,这两个RDD partition数量和元素数量相同;

zipPartitions将多个RDD按照partition组合成新的RDD ,该函数需要组合的RDD具有相同的分区数,但对元素没有要求;

zipWithIndex将rdd元素及其索引进行拉链,索引始于0,组合成键值对;

zipWithUniqueId每个元素对应唯一id值,id不一定和真实元素索引一致,唯一id生成算法:

每个分区第一个元素唯一id为:该分区索引号,每个分区第N个元素唯一ID值为:(前一个元素唯一ID值) + (该RDD总分区数)

val rd1 = rdd3.map(_._1)

val rd2 = rdd3.map(_._2)

val rd3 = rd1.zip(rd2)

rd3.collect()

# rd1分区元素分布

val rd4 = rd1.mapPartitionsWithIndex{

(partIdx,iter)=>{

var part_map = scala.collection.mutable.Map[String,List[String]]()

while(iter.hasNext){

var elem = iter.next()

var part_name = "part_" + partIdx

if(part_map.contains(part_name)){

var elems = part_map(part_name)

elems ::= elem

part_map(part_name) = elems

}else{

part_map(part_name) = List[String]{elem}

}

}

part_map.iterator

}

}

# rd2分区元素分布

val rd5 = rd2.mapPartitionsWithIndex{

(partIdx,iter)=>{

var part_map = scala.collection.mutable.Map[String,List[Int]]()

while(iter.hasNext){

var elem = iter.next()

var part_name = "part_" + partIdx

if(part_map.contains(part_name)){

var elems = part_map(part_name)

elems ::= elem

part_map(part_name) = elems

}else{

part_map(part_name) = List[Int]{elem}

}

}

part_map.iterator

}

}

# rd1与rd2做zipPartitions

val rd6 = rd1.zipPartitions(rd2){

(iter1,iter2)=>{

var result = List[String]()

while(iter1.hasNext && iter2.hasNext) {

result ::= (iter1.next() + "_" + iter2.next())

}

result.iterator

}

}

rd6.collect()

val rd7 = rd1.zipWithIndex

rd7.collcect()

val rd8 = rd1.zipWithUniqueId

# 唯一id和索引值并不一定相同验证

val rd9 = rd7.join(rd8)

rd9.collect()

15. coalesce repartition

repartition(numPartitions:Int):RDD[T]和coalesce(numPartitions:Int,shuffle:Boolean=false):RDD[T]

repartition是coalesce接口中shuffle为true简易实现,假设分区前N个分区,分区后M个分区:

N

N>M(相差不多) 将N个分区若干分区合并为一个新的分区,最终合并为M个分区,shuffle是指为false,父RDD与子RDD是窄依赖。

N>M(相差悬殊)若将shuffle设置为false,父子RDD是窄依赖,处于统一stage并行度不够,为此可以将shuffle设置为true。

 

三、spark action操作

1. reduce

reduce对数据集元素进行聚合

val r1 = sc.parallelize(1 to 9,3)

val r2 = r1.reduce(_+_)

2. collect

collect将数据集中所有元素以array返回。

3. count

返回数据集元素个数

r1.count()

4. first take

first返回第一个元素,take(n)返回一个包含数据集中前n个元素数组。

r1.first()

r1.take(3)

5. takeSample takeOrdered top

takeSmaple(withReplacement,num,[seed])返回包含随机的num个元素的数组,withReplacement是否抽样放回,num抽样个数,seed是随机种子;

takeOrdered(n,[ordering])返回i包含随机n个元素数组,按照升序排列;top是按照降序排列。

6. saveAsTextFile

数据集写道文本文件中

7. countByKey

对于KV pairs类型RDD,返回一个(K,Int)的map,Int为K个数。查看是否存在数据倾斜

8. foreach

对数据集每个元素都执行func函数。

 

你可能感兴趣的:(hadoop)