Spark RDD

一、RDD 是什么

​ RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是 Spark 中最基本的数据抽象。代码中是一个抽象类,它代表一个不可变、可分区、里面的元素可并行计算的集合

不可变:RDD 一旦创建就把它封装的计算都确定下来了,不能再对其进行改变,只能产生新的 RDD,这样的好处是可以让在任务失败的时候,顺着 RDD 中的血缘推回到失败前的状态,直接重新计算,而不需要重新复制数据。

可分区和并行计算:RDD 内部是进行分区的,这样才可以实现并行计算。

二、RDD 体现了装饰者的设计模式

例:wordCount

代码:

object WordCount {
    def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setAppName("wordCount").setMaster("local[*]")
        val sc = new SparkContext(conf)
        //获取文件
        val text: RDD[String] = sc.textFile("spark-review/input/wordcount.txt")
        //切割
        val word: RDD[String] = text.flatMap(line => line.split(" "))
        //映射成 (word, one) 
        val word_one: RDD[(String, Int)] = word.map(word => (word, 1))
        //统计每个 word 的个数
        val word_count: RDD[(String, Int)] = word_one.reduceByKey(_ + _)
        //输出
        word_count.foreach(x => println(x._1, x._2))
    }
}

图解:可以看到每个 RDD 的内部包含了它的父 RDD,这就是 RDD 的依赖

在这里插入图片描述

二、RDD 的属性

  1. 一组分区,即数据集的基本组成单位,分区可以提高并行度;

  2. 一个计算每个分区的函数;

  3. RDD 之间的依赖关系,依赖关系记录下来了 RDD 的血缘;

    依赖可以分为宽依赖和窄依赖宽依赖是指下游 RDD 的分区是从上游父 RDD 多个分区中计算得到的(一对多),窄依赖是指下游每个 RDD 的分区值对应上游父 RDD 的一个分区(一对一)。

  4. 一个 Partitioner,即 RDD 的分区函数;

  5. 一个存储每个 Partition 优先位置的列表,把计算发送到距离数据最近的地方可以提高效率。

    优先位置:Executor > 节点本地化 > 机架本地化

三、RDD 的创建

3.1 从集合中创建

​ 两个函数:makeRDD() 和

​ 源码:

def parallelize[T: ClassTag](		//从集合中创建
    seq: Seq[T],
    numSlices: Int = defaultParallelism): RDD[T] = withScope {
    assertNotStopped()
    new ParallelCollectionRDD[T](this, seq, numSlices, Map[Int, Seq[String]]())
}


def makeRDD[T: ClassTag](		
    seq: Seq[T],
    numSlices: Int = defaultParallelism): RDD[T] = withScope {
    parallelize(seq, numSlices)		//底层调用的还是 parallelize() 
}

​ 源码中的 numSlices 参数代表了分区个数,默认值如下定义:

override def defaultParallelism(): Int = {
    //可以看到,分区个数与 CPU 核数有关
    conf.getInt("spark.default.parallelism", math.max(totalCoreCount.get(), 2))
}

​ 也可以手动设定 numSlices 的值,例:

object CreateRDD {
    def main(args: Array[String]): Unit = {
        val conf: SparkConf =new SparkConf()
        					.setMaster("local[*]")
        					.setAppName("createRDD")
        val sc: SparkContext = new SparkContext(conf)

        val listRDD1: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 8)
        listRDD1.saveAsTextFile("output")   //保存了 8 个文件
    }
}

3.2 按照指定步长创建 RDD

3.2 从外部文件中创建

​ 源码:

def textFile(
    path: String,
    //注意这里是 minPartition,所以有可能分区的结果会大于这个数
    minPartitions: Int = defaultMinPartitions): RDD[String] = withScope {
    assertNotStopped()
    //这里用的是 hadoop 的读取文件规则
    hadoopFile(path, classOf[TextInputFormat], classOf[LongWritable], classOf[Text],
               minPartitions).map(pair => pair._2.toString).setName(path)
}

// defaultMinPartitions 源码,默认值与 2 取较小者
def defaultMinPartitions: Int = math.min(defaultParallelism, 2)

// defaultParallelism 源码
override def defaultParallelism(): Int = {
    //可以看到,分区个数与 CPU 核数有关
    conf.getInt("spark.default.parallelism", math.max(totalCoreCount.get(), 2))
}

​ 例:

val textRDD: RDD[String] = sc.textFile("spark-review/input/file.txt"2)   //file.txt: 123456(只有这 6 个数字)
textRDD.saveAsTextFile("output")    //结果存储了两个文件

​ 但是注意,这是从外部文件创建的 RDD,所以必然会经过 hdfs,这里就会采用 hdfs 的分片规则,这就导致了最后的分区个数不是在函数里指定多少就是多少,而是与 hdfs 的分片规则有关:

//hadoop分片规则核心源码

//分片期望值大小 = 总的文件大小/分片数量,这里的 numSplits 对应的就是 minPartition
long goalSize = totalSize / (numSplits == 0 ? 1 : numSplits);
//最小分片数量,默认为 1
long minSize = Math.max(job.getLong("mapred.min.split.size", 1), minSplitSize);
//默认为 128M,与磁盘速度有关
long blockSize = file.getBlockSize();
//totalSize / splitSize 即为分区个数,如 5 / 2 = 2...1 ,所以分为 2,2,1,即三个分区
long splitSize = computeSplitSize(goalSize, minSize, blockSize);
//分片核心函数
protected long computeSplitSize(long goalSize, long minSize, long blockSize) {
	//分片大小的决定性公式:max(最小分片大小,min(期望分片大小,块的大小))
     //如果文件大小小于 blockSize,就会返回 goalSize
    return Math.max(minSize, Math.min(goalSize, blockSize));	
}

​ 所以就有了下面的例子:

val textRDD: RDD[String] = sc.textFile("spark-review/input/file.txt")   //file.txt: 12345(只有这 6 个数字)
textRDD.saveAsTextFile("output")    //结果存储了三个文件

3.3 range 函数

val rangeRDD: RDD[Long] = sc.range(1, 100, 4, 1)
rangeRDD.saveAsTextFile("output")//1, 5, 9, 13, ...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值