Spark 数据全局排序实现以及RangePartitioner的使用示例

来源:互联网 发布:python 期货交易接口 编辑:程序博客网 时间:2024/05/18 01:12

使用Java 随机数类随机生成0到Integer.MAX_VALUE之间的10GB文件,然后使用Spark计算框架进行全局排序。

实现思路:可以局部排序(局部数据是全局数据的某一个范围)最后合并到全同一个文件,保证全局有序,这样可以设置一个reduce任务实现,但是对于更大量的数据容易出现OOM。如果不合并到同一个文件的话,可以将每一个分区有序的数据输出到磁盘。最后借鉴Kafka的数据管理方式建立稀疏索引方便以后的数据访问。


1:定义一个Partitioner保证某一范围内的所有数据都在同一个分区。

package com.daxinimport org.apache.spark.Partitioner/**  * Created by Daxin on 2017/9/13.  */class SortPartitoner(num: Int) extends Partitioner {  override def numPartitions: Int = num  val partitionerSize = Integer.MAX_VALUE / num + 1  override def getPartition(key: Any): Int = {    val intKey = key.asInstanceOf[Int]    intKey / partitionerSize  }}

该分区根据数据范围划分为num个子范围,然后将每个数字分配到对应的子范围中,这种情况下当数据在各个子范围分布均匀时候可以表现良好。但是当数据严重聚集时候,会发生数据倾斜。当存在数据倾斜时候可以使用Spark提供的 RangePartitioner分区器进行分区。该分区器会根据RDD进行采样然后根据数据的实际情况合理的划分子范围(此时的每一个子范围区间长度可能是不相等的,要根据具体数据而定)。这样可以缓解数据倾斜的发生。例如:


例如当我们数据如下:

Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 22, 33, 43, 26, 50, 81, 54, 76, 94, 100)
如果按照每个子范围长度为25的话。划分4个区间的话,明显第一个子范围就会发生数据倾斜,而对于RangePartitioner划分子范围的话,划分区间可以是:

0-6 //范围区间内数据为:1, 2, 3, 4, 56-11 //范围区间内数据为:6, 7, 8, 9, 1011-50//范围区间内数据为:11, 22, 26, 33, 4350-100//范围区间内数据为:50, 54, 76, 81, 94, 100

这个划分就比较均匀。

2:分区内部排序,保证分区内有序

object Sort {  def main(args: Array[String]) {    val conf = new SparkConf()    val sc = new SparkContext(conf)    //Persist this RDD with the default storage level (MEMORY_ONLY).    val numbers = sc.textFile("/random.txt").flatMap(_.split(" ")).map(x => (x.toInt, 1)).cache()    val result = numbers.repartitionAndSortWithinPartitions(new SortPartitoner(numbers.partitions.length)).map(x=>x._1)    result.saveAsTextFile("/bigdatasort")    sc.stop()  }}


要点: 

使用repartitionAndSortWithinPartitions算子进行分区并分区内部排序,而不是自行先分区在排序,这样不如repartitionAndSortWithinPartitions效率高,具体原因见repartitionAndSortWithinPartitions这一篇博文。







基于Hadoop实现的全局排序可以参考:

三种方法实现Hadoop(MapReduce)全局排序(1)

三种方法实现Hadoop(MapReduce)全局排序(2)