Spark | 分组排序

记录下Spark分组排序时遇到的问题,需求问题如下:

当前有HDFS文件数据内容为:

(字段顺序为手机号-phone、用户ID-userId、线路-line、时间戳-clientTimestamp),然后读取HDFS文件内容生成rdd,其中rdd的格式为( (phone,userId,line))

130xxxxxxxx&1980098385910287364&1004&1568528894
130xxxxxxxx&2233727976012209153&1005&1568555149
130xxxxxxxx&2320761066814396420&1002&1568515662
130xxxxxxxx&1956824763490556928&1005&1568538553
130xxxxxxxx&2255034634315076610&1001&1568528871

1、使用Spark DataFrame完成分组排序(推荐)

val userLineDF = rdd.map( x=>{((x._2,x._3),1)}).reduceByKey(_+_).map(x=>(x._1._1,x._1._2,x._2)).toDF("userId","line","rideCount")

val dfTop1 = userLineDF.withColumn("rank",row_number().over(Window.partitionBy("userId").orderBy(desc("rideCount")))).where("rank=1").select("userId","line").rdd.map(x=>{
  val v = x.getString(1)+broadcastTuple.value._2+updatetime
  (x.getString(0),v)
})

2、使用Spark RDD之groupByKey()完成分组排序

rdd.map( x=>{((x._2,x._3),1)}).reduceByKey(_+_).map(x=>{
  (x._1._1,(x._1._2,x._2))
}).groupByKey().map( x => {
  val lineBuffer = x._2.toBuffer
  val sortByLineCountBuffer = lineBuffer.sortBy(_._2)
  (x._1,sortByLineCountBuffer.reverse.take(2))
}).flatMap(x => {
  val i = x._2
  for (e <- i) yield (x._1, e._1, e._2)
}).collect().take(100).foreach(println(_))

3、使用Spark aggregateByKey()完成分组排序(推荐)

rdd.map( x=>{((x._2,x._3),1)}).reduceByKey(_+_).map(x=>{
  (x._1._1,(x._1._2,x._2))
}).aggregateByKey(scala.collection.mutable.Set[(String, Int)]())((set, item) => {
  set +=  item
}, (set1, set2) => set1 union set2 ).map( x => {
  val lineBuffer = x._2.toBuffer
  val sortByLineCountBuffer = lineBuffer.sortBy(_._2)
  (x._1,sortByLineCountBuffer.reverse.take(2))
}).flatMap(x => {
  val i = x._2
  for (e <- i) yield (x._1, e._1, e._2)
}).collect().take(100).foreach(println(_))

4、为什么推荐使用aggregateByKey()代替groupByKey()?

首先groupByKey()是把分区中的所有键值对都进行移动,是对RDD中的所有数据做shuffle,然后根据不同的Key映射到不同的partition中再进行aggregate。这样会导致集群节点之间传输效率较低,开销较大。

其次aggregateByKey()使用map-side预聚合的shuffle操作,即先对每个partition中的数据根据不同的Key进行aggregate,然后将结果进行shuffle,完成各个partition之间的aggregate。相当于在map端进行了聚合的操作,可理解为mapreduce中进行combiner。这样的话和groupByKey()相比减少很多运算量。

5、针对上述aggregateByKey()的解析

aggregateByKey(参数1)(参数2,参数3)

概述:对于data的某个key,参数1为初始化值,在参数2的函数中,初始值和该key的每一个value传入函数进行操作,所有返回的结果在参数3中进行规约。

参数1

  scala.collection.mutable.Set[(String, String)]() new 了一个空的set集合,做为初始值

参数2

(set, item) => { set += item }

一个类似于map的映射函数,将该key的每一个value (在本案例是(线路,线路访问次数))作为item,将其放入set中并返回。

可知某个key的所有value都会返回一个含有该value的set

参数3

(set1, set2) => set1 union set2

该key的所有value得到的set进行union规约。并返回

发布了44 篇原创文章 · 获赞 11 · 访问量 5446

猜你喜欢

转载自blog.csdn.net/Sampson_Hugo/article/details/103390592
今日推荐