处理数据时,比我们想对一个年级的所有语文考试成绩先按班级分类,再在每个班级里按成绩排名,最终每个班级的数据保存为一个文件,这就要用到spark分区加排序的技巧
数据为DF格式时
代码
val spark =SparkSession.builder().config(new SparkConf()).getOrCreate()
val sc =spark.sparkContext
val data = sc.parallelize(Array((100,2),(200,1),(300,3),(100,4),(200,4),(300,8) ,(200,5),(300,6),(100,5),(100,0),(200,6),(200,-1)))
import spark.implicits._
val DF_sort_partition =data.toDF("key","value")
.sort(desc("value"))
.write
.partitionBy("key")
.mode(SaveMode.Overwrite)
.parquet("develop/wangdaopeng/partitionTest")
val s1 =spark.read.parquet("develop/wangdaopeng/partitionTest/key=100").show()
val s2 =spark.read.parquet("develop/wangdaopeng/partitionTest/key=200").show()
结果的保存形式
因为我们是根据"key"字段来保存,所以保存结果的目录是key=xx的形式,每个key对应一个分区
结果内容
key=100:
key=200:
结果完全符合预期
数据为RDD格式时
如果像DF格式那样写,是达不到预期的效果的,比如下列代码
先排序再分区
data
.sortBy(_._2)
.partitionBy(new HashPartitioner(3))
或者先分区再排序
data
.partitionBy(new HashPartitioner(3))
.sortBy(_._2)
结果都是不符合预期的
对于RDD,一种简单的先分区再再分区里进行排序的方法如下,先repartition,RDD的partitionBy默认都是根据key值来进行的(对于pair 对,就是第一个元素),mapPartitions 输入和返回的都是一个迭代器,排序的方法在于先把iterator转化为list 排序再以iterator的形式返回
val res =data
.partitionBy(new HashPartitioner(3))
.mapPartitions{x=>
x.toList.sortBy(_._2).toIterator
}
.saveAsTextFile(path)
结果