Spark数据结构RDD

什么是RDD

RDD叫做弹性分布式数据集,是Spark中基本的数据抽象,在代码中是一个抽象类,它是一个弹性的、不可变的、可分区的,并且可并行计算的集合.

RDD的特性

  • 一组分区;对于RDD来说,每个分区被一个计算任务处理,并决定了并行计算的粒度;
  • 一个计算每个分区的函数;Spark中的计算是以分片为单位,每个RDD都是实现compute函数来实现计算;
  • RDD之间的依赖关系;RDD每次转换都会生成一个新的RDD,即RDD之间就会形成前后依赖关系,在部分分区丢失时,Spark可以通过依赖关系重新计算丢失的分区数据,而不是计算所有的分区的数据;
  • 一个分片函数;对与键值对的RDD,需要一个分区器,来控制数据的流向,并且决定了分区的个数;
  • 一个列表,存储每个分区的优先未知,这个是根据了移动数据不如移动计算的理念,尽可以的把计算和数据在同一机器中,这样可以节约网络流量。

RDD创建

  • 从集合中创建
val rdd: RDD[Int] = sc.parallelize(Array(1, 2, 3, 4, 5, 6, 7, 8,9,10))
  • 从外部存储系统的数据创建
val lineWordRdd: RDD[String] = sc.textFile("inputtxt")
  • 从其他RDD创建
val value: RDD[(String, Int)] = sc.textFile("data/test.txt").flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey(_ + _)

RDD分区

RDD自定义分区

object WordCount {
  def main(args: Array[String]): Unit = {

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("WC")
    val sc: SparkContext = new SparkContext(conf)
    val value: RDD[(String, Int)] = sc.textFile("data/1.txt")
      .flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey(_ + _)
      .partitionBy(new CustomPartitioner(2))
    value.foreach(println)
    sc.stop()

  }

  class CustomPartitioner(num:Int) extends Partitioner{

    override def numPartitions: Int = num

    override def getPartition(key: Any): Int = {
      if(key == 1){
        0
      }  else{
        1
      }
    }
  }

}

我们自定义的分区函数,继承Partitioner,传递分区的个数,并且自己实现数据分区的策略。自己调用partitionBy(new CustomPartitioner(2)),让RDD调用我们自己的分区函数。

Spark分区器源码

abstract class Partitioner extends Serializable {
  def numPartitions: Int
  def getPartition(key: Any): Int
}
  def defaultPartitioner(rdd: RDD[_], others: RDD[_]*): Partitioner = {
    val rdds = (Seq(rdd) ++ others)
    val hasPartitioner = rdds.filter(_.partitioner.exists(_.numPartitions > 0))

    val hasMaxPartitioner: Option[RDD[_]] = if (hasPartitioner.nonEmpty) {
      Some(hasPartitioner.maxBy(_.partitions.length))
    } else {
      None
    }

    val defaultNumPartitions = if (rdd.context.conf.contains("spark.default.parallelism")) {
      rdd.context.defaultParallelism
    } else {
      rdds.map(_.partitions.length).max
    }

    // If the existing max partitioner is an eligible one, or its partitions number is larger
    // than or equal to the default number of partitions, use the existing partitioner.
    if (hasMaxPartitioner.nonEmpty && (isEligiblePartitioner(hasMaxPartitioner.get, rdds) ||
        defaultNumPartitions <= hasMaxPartitioner.get.getNumPartitions)) {
      hasMaxPartitioner.get.partitioner.get
    } else {
      new HashPartitioner(defaultNumPartitions)
    }
  }

如果RDD的最大分区数据大于等于默认的分区数,取RDD的最大分区数;否则取默认分区数。
在这里插入图片描述

  • HashPartitioner:默认分区函数,按key求取hash值,再对hash值除以分区个数取余,如果余数<0,则用余数+分区的个数,最后返回的值就是这个key所属的分区ID。
  • RangePartitioner:基于范围的分区,用于可排序记录的分区。它通过对输入的RDD进行采样来确定分区的边界,然后根据键的大小将元素分配到相应的分区。默认情况下,它按升序分区,也可以设置降序分区。它还可以设置每个分区的采样点的数量。

RangePartitioner实现过程:
1.先从整个RDD中采用水塘抽样算法,抽取样本数据,将样本数据排序,计算出峰哥分区最大的key值,形成一个Array[Key]类型的数组变量rangeBounds;
2.判断key在rangeBounds中所处的范围,给出该key在下一个RDD中分区id下标,该分区器要求RDD中key类型是必须可以排序的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值