Spark——Partition的分区规则和分区数

Partition的分区规则

只有Key-Value类型的RDD才有Hash分区的,非Key-Value类型的RDD分区的是范围分区

以下代码以foreachPartition为例

1、 范围分区(RangePartitioner)

RangePartitioner基于抽样的思想来对数据进行分区

RangePartitioner作用:将一定范围内的数映射到某一个分区内,尽量保证每个分区中数据量的均匀,而且分区与分区之间是有序的,一个分区中的元素肯定都是比另一个分区内的元素小或者大,但是分区内的元素是不能保证顺序的。简单的说就是将一定范围内的数映射到某一个分区内。实现过程为:

第一步:先重整个RDD中抽取出样本数据,将样本数据排序,计算出每个分区的最大key值,形成一个Array[KEY]类型的数组变量rangeBounds;

第二步:判断key在rangeBounds中所处的范围,给出该key值在下一个RDD中的分区id下标;该分区器要求RDD中的KEY类型必须是可以排序的

object Spark04 {
  def main(args: Array[String]): Unit = {
    //1.创建SparkConf并设置App名称,使用本地模式开启两核
    val conf = new SparkConf().setMaster("local[2]").setAppName("myjob")
    //2.创建SparkContext,该对象是提交Spark App的入口
    val sc = new SparkContext(conf)
    val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 2, 3), 2)
    //不是key-value所以是范围分区
    rdd.foreachPartition(f => println(f.toList))
    // List(1, 2, 3)
    //List(4, 5, 2, 3)
  }
}

2、 Hash分区(HashPartitioner)

分区规则为

def getPartition(key: Any): Int = key match {
  case null => 0
  case _ => Utils.nonNegativeMod(key.hashCode, numPartitions)
}
def nonNegativeMod(x: Int, mod: Int): Int = {
  val rawMod = x % mod
  rawMod + (if (rawMod < 0) mod else 0)
}

HashPartitioner分区的原理:对于给定的key,计算其hashCode,并除以分区的个数取余,如果余数小于0,则用余数+分区的个数(否则加0),最后返回的值就是这个key所属的分区ID。

object Spark05 {
  def main(args: Array[String]): Unit = {
    //1.创建SparkConf并设置App名称,使用本地模式开启两核
    val conf = new SparkConf().setMaster("local[2]").setAppName("myjob")
    //2.创建SparkContext,该对象是提交Spark App的入口
    val sc = new SparkContext(conf)
    val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 2, 3), 2)
    //变成key-value
    rdd.map((_, 1)).groupByKey(2).foreachPartition(f => println(f.toList))
    // List((4,CompactBuffer(1)), (2,CompactBuffer(1, 1)))
    //List((1,CompactBuffer(1)), (3,CompactBuffer(1, 1)), (5,CompactBuffer(1)))
  }
}

3、 自定义分区

自己写一个类继承Partitioner并且重写numPartitions和getPartition方法

//自定义分区方法
class MyPartition(numPartition:Int) extends Partitioner{
	//重写分区数
  override def numPartitions: Int = {
    numPartition
  }
	//分区条件
  override def getPartition(key: Any): Int = {
   if( key.asInstanceOf[Int] <5) 0 else 1
  }
}

object Spark06 {
  def main(args: Array[String]): Unit = {
    //1.创建SparkConf并设置App名称,使用本地模式开启两核
    val conf = new SparkConf().setMaster("local[2]").setAppName("myjob")
    //2.创建SparkContext,该对象是提交Spark App的入口
    val sc = new SparkContext(conf)
    val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 2, 3), 2)
    //自定义分区
                                                    //repartition会改变前面的分区数,变成一个区
    rdd.map((_, 1)).partitionBy(new MyPartition(2)) /*.repartition(1)*/ .foreachPartition(f => println(f.toList))
    //List((5,1))
    //List((1,1), (2,1), (3,1), (4,1), (2,1), (3,1))  
  }
}

4、 默认分区数

对于makeRDD和parallelize

如果不给分区数,就会按照local[*]里的核数给定分区数,默认不超过最大核数

object Spark01 {
  def main(args: Array[String]): Unit = {
    //给*就是按照电脑cpu最大核数
    val conf = new SparkConf().setMaster("local[*]").setAppName("myjob")
    val sc = new SparkContext(conf)
    //有默认分片值,是电脑cpu最大核数,我是16核所以有16个分区
    val rdd=sc.parallelize(List(1,2,3,4,5))
    rdd.foreachPartition(f=> println(f.toList))
  }
}

在这里插入图片描述

对于testFile

不给分区数,系统给的分区数就是2,如果给的分区数大于2,就按照你给的分区数

object Spark01 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("myjob")
    val sc = new SparkContext(conf)
    //有默认分片值2
    val rdd=sc.textFile("file:///C:\\Users\\wuyanxiang\\study\\hadoop\\spark01\\src\\main\\scala\\njbdqn\\words")
    rdd.foreachPartition(f=> println(f.toList))
  }
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值