美图欣赏:
一.背景
我们都知道Spark内部提供了
HashPartitioner
和RangePartitioner
两种分区策略,这两种分区策略在很多情况下都适合我们的场景。但是有些情况下,Spark内部不能符合咱们的需求,这时候我们就可以自定义分区策略(注:默认是HashPartitioner
分区)
二.如何实现
要实现自定义的分区器,你需要继承 org.apache.spark.Partitioner 类并实现下面三个方法。
numPartitions: Int
:返回创建出来的分区数。
getPartition(key: Any): Int
:返回给定键的分区编号(0到numPartitions-1)。
三.数据模板
这里,主要是为了测试自定义分区。数据量相对不是特别大
里面一共有五门课程:ui ,java , h5 , bigdata , android
四.需求
自定分区,数据中有不同的学科,将输出的一个学科生成一个文件。
思路,从需求进行分析,然后通过不同算子进行转换。
五.代码实现
import java.net.URL
import org.apache.spark.rdd.RDD
import org.apache.spark.{HashPartitioner, Partitioner, SparkConf, SparkContext}
import scala.collection.mutable
/**
* 自定义分区器实现自己的分区逻辑
*/
object CustomPartitioner_2 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[2]")
val sc = new SparkContext(conf)
sc.setLogLevel("ERROR")
// 获取数据并切分生成元组
val tups = sc.textFile("D:\\scala学习笔记\\day10\\datasource")
.map(line => (line.split("\t")(1), 1))
println(tups.collect.toBuffer)
// 聚合生成各个模块的访问量
val aggred: RDD[(String, Int)] = tups.reduceByKey(_ + _).cache()
println(aggred.collect.toBuffer)
// 获取学科信息并返回学科信息及url和访问量
val subjectAndUrlAndCount: RDD[(String, (String, Int))] = aggred.map(tup => {
val url = tup._1
val count = tup._2
val subject = new URL(url).getHost
(subject, (url, count))
}).cache()
println(subjectAndUrlAndCount.collect.toBuffer)
// 调用默认的分区器查看分区情况(HashPartitioner)
// val partitioned: RDD[(String, (String, Int))] = subjectAndUrlAndCount.partitionBy(new HashPartitioner(5))
// partitioned.saveAsTextFile("D:\\scala学习笔记\\day101\\")
//获取到所有的学科信息
val subjects: RDD[String] = subjectAndUrlAndCount.keys.distinct
val subjectArr: Array[String] = subjects.collect()
//调用自定义分区器进行分区
val partition: RDD[(String, (String, Int))] = subjectAndUrlAndCount.partitionBy(new SubjectPartioner(subjectArr))
//分组排序并取top3
val result: RDD[(String, (String, Int))] = partition.mapPartitions(it => {
it.toList.sortWith(_._2._2 > _._2._2).take(3).iterator
})
result.saveAsTextFile("D:\\scala学习笔记\\day11")
}
}
/**
* 自定义分区器
*/
class SubjectPartioner(subjectArr: Array[String]) extends Partitioner {
//获取分区数
override def numPartitions: Int = subjectArr.length
//获取分区号
override def getPartition(key: Any): Int = {
val subjectAndNum = new mutable.HashMap[String, Int]()
//计数器
var i = 0
for (subject <- subjectArr) {
subjectAndNum += (subject -> i)
i += 1
}
subjectAndNum.getOrElse(key.toString, 0)
}
}
最终结果:
拿出来其中一个分区数据看看:
最终统计出来了,前三 (符合需求)
————保持饥饿,保持学习
Jackson_MVP