- def numPartitions --分区器的分区数量
- def getPartition(key: Any): Int ---获取某一个key的分区号
Spark中非常重要的一个分区器,也是默认分区器,默认用于90%以上的RDD相关API上
功能:
源码HashPartitioner定义如下
class HashPartitioner(partitions: Int) extends Partitioner {
require(partitions >= 0, s"Number of partitions ($partitions) cannot be negative.")
//分区的总数,可以利用构造函数从外部传入
def numPartitions: Int = partitions
//依据key进行分区的划分,就是划分每个key属于哪个分区
def getPartition(key: Any): Int = key match {
case null => 0
case _ => Utils.nonNegativeMod(key.hashCode, numPartitions)
}
override def equals(other: Any): Boolean = other match {
case h: HashPartitioner =>
h.numPartitions == numPartitions
case _ =>
false
}
override def hashCode: Int = numPartitions
}
SparkCore中除了HashPartitioner分区器外,另外一个比较重要的已经实现的分区器,主要用于RDD的数据排序相关API中
比如sortByKey底层使用的数据分区器就是RangePartitioner分区器;
第一步:先从整个RDD中抽取出样本数据,将样本数据排序,计算出每个分区的最大key值,形成一个Array[KEY]类型的数组变量rangeBounds;
第二步:判断key在rangeBounds中所处的范围,给出该key值在下一个RDD中的分区id下标;该分区器要求RDD中的KEY类型必须是可以排序的,
需求,单词频率统计 aA-nN在0分区,其他字母开头的在1分区,其他在2分区
package src.main.scala
import org.apache.spark.{Partitioner, SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object _PartitionDemo extends App {
val conf = new SparkConf().setAppName("wordcount").setMaster("local")
val sc = new SparkContext(conf)
val lines: RDD[String] = sc.textFile("D:\\tmp", 2)
val words: RDD[String] = lines.flatMap(_.split(" "))
val tuples: RDD[(String, Int)] = words.map((_, 1))
val myPartitioner = new WordCountPartition(3)
val summed: RDD[(String, Int)] = tuples.reduceByKey(_ + _)
//在此处调用自定义的分区器
val sorted: RDD[(String, Int)] = summed.sortBy(_._2, ascending = false).partitionBy(myPartitioner)
sorted.saveAsTextFile("output5")
sc.stop()
}
//自定义分区器,继承Partitioner
class WordCountPartition(numPartition: Int) extends Partitioner {
//numPartitions代表分区的数目,可以利用构造函数传入,也可以设定为固定值
override def numPartitions: Int = numPartition
//依据输入内容计算分区id(就是属于哪个分区)
override def getPartition(key: Any): Int = {
//key的类型为Any,需要转换为String
var str: String = key.toString
//取字符串的第一个字符的内容
val first: String = str.substring(0,1)
//matches的参数为正则表达式
if (first.matches("[A-Na-n]")) {
0
} else if (first.matches("[o-zO-Z]")) {
1
} else {
2
}
}
}
20161123101523 http://java.learn.com/java/javaee.shtml
20161123101523 http://java.learn.com/java/javaee.shtml
20161123101523 http://ui.learn.com/ui/video.shtml
20161123101523 http://bigdata.learn.com/bigdata/teacher.shtml
20161123101523 http://android.learn.com/android/video.shtml
20161123101523 http://h5.learn.com/h5/teacher.shtml
20161123101523 http://h5.learn.com/h5/course.shtml
20161123101523 http://bigdata.learn.com/bigdata/teacher.shtml
* 自定义分区器
* 需求:数据中有不同的学科,统计每个学科的访问量,将输出的一个学科生成一个文件
*
* (ui.learn.com,86)
import java.net.URL
import org.apache.spark.rdd.RDD
import org.apache.spark.{Partitioner, SparkConf, SparkContext}
import scala.collection.mutable
object _PartitionDemo2 extends App {
val conf = new SparkConf().setAppName("SubjectDemo").setMaster("local")
val sc = new SparkContext(conf)
//读取文件进行切分
val lines: RDD[(String, Int)] = sc.textFile("access.txt", 2).map(x => {
//利用空格切分每一行
val fields: Array[String] = x.split("[\\s]+")
//提取第二个字段
val str: String = fields(1)
//借用java的URL对象获取host
val url = new URL(str)
val host: String = url.getHost
//构建元组,1是为了方便后面统计
(host, 1)
})
//统计个host的数量
val sumed: RDD[(String, Int)] = lines.reduceByKey(_ + _).cache()
//获取完全不同的host的数组,作为后续分区的依据,因为题目要求每个host分一个区
val subjects: Array[String] = sumed.keys.distinct.collect
//创建自定义分区器对象
var partioner = new SubjectPartitioner(subjects)
//调用自定义分区器
val value: RDD[(String, Int)] = sumed.partitionBy(partioner)
//输出结果
value.saveAsTextFile("out3")
sc.stop()
}
//创建自定义分区器.其中分区总数量由subjects计算而来,就是完全不同的host的数量
class SubjectPartitioner(subjects: Array[String]) extends Partitioner {
//创建一个map用于存储学科和分区号
val subject = new mutable.HashMap[String, Int]()
var i = 0
//实际构建map,实质创建key和分区号的对应关系
for (s <- subjects) {
subject += (s -> i)
//分区号自增
i += 1
}
override def numPartitions: Int = {
//总分区数目
subjects.length
}
//分区号等于key所对应的value的值
override def getPartition(key: Any): Int = subject.getOrElse(key.toString, 0)
}