使用Java 随机数类随机生成0到Integer.MAX_VALUE之间的10GB文件,然后使用Spark计算框架进行全局排序。
实现思路:可以局部排序(局部数据是全局数据的某一个范围)最后合并到全同一个文件,保证全局有序,这样可以设置一个reduce任务实现,但是对于更大量的数据容易出现OOM。如果不合并到同一个文件的话,可以将每一个分区有序的数据输出到磁盘。最后借鉴Kafka的数据管理方式建立稀疏索引方便以后的数据访问。
1:定义一个Partitioner保证某一范围内的所有数据都在同一个分区。
package com.daxin
import 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, 5
6-11 //范围区间内数据为:6, 7, 8, 9, 10
11-50//范围区间内数据为:11, 22, 26, 33, 43
50-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)