在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)
map输入rdd中每个元素,mapPartition输入是rdd每个分区数据
map一对一,flatMap一对多
mapValues KV键值对rdd,key不变
mapPartitionsWithIndex func函数(Int,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()
val rdd6 = rdd2.filter(f=>f.length > 5)
rdd6.collect()
sample(withReplacement,fraction,seed)是根据给定随机种子seed,随机抽样出数量为frac的数据。withReplacement:是否放回抽样
val rdd7 = rdd2.sample(false, 0.1, 2)
rdd7.collect()
两个集合数据类型一致
val rdd8 = rdd6.union(rdd7)
rdd8.collect()
返回两个集合的交集
val rdd9 = rdd8.intersection(rdd6)
rdd9.collect()
返回两个集合去重后新集合
val rdd10 = rdd9.union(rdd9).distinct()
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()
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()
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()
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()
pipe(command,[envVars])是以shell命令处理rdd
rdd24.pipe("head -n 1").collect()
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()
subtract(other:RDD[T]):RDD[T]是对RDD进行减法操作
val rdd27 = rdd18.subtract(rdd25)
rdd27.collect()
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()
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。 reduce对数据集元素进行聚合 collect将数据集中所有元素以array返回。 返回数据集元素个数 first返回第一个元素,take(n)返回一个包含数据集中前n个元素数组。 takeSmaple(withReplacement,num,[seed])返回包含随机的num个元素的数组,withReplacement是否抽样放回,num抽样个数,seed是随机种子; takeOrdered(n,[ordering])返回i包含随机n个元素数组,按照升序排列;top是按照降序排列。 数据集写道文本文件中 对于KV pairs类型RDD,返回一个(K,Int)的map,Int为K个数。查看是否存在数据倾斜 对数据集每个元素都执行func函数。 三、spark action操作
1. reduce
val r1 = sc.parallelize(1 to 9,3)
val r2 = r1.reduce(_+_)
2. collect
3. count
r1.count()
4. first take
r1.first()
r1.take(3)
5. takeSample takeOrdered top
6. saveAsTextFile
7. countByKey
8. foreach