RDD详解课堂笔记

sc.textFile(“里面放路径”) 这两个都是创建一个RDD
sc.parallelize(里面放集合)

val arr = Arry(1,2,3,4,5,6,7,8,9)
val rdd = sc.parallelize(arr)
rdd.partitions.length              这是查看有几个分区
用parallelize创建分区的时候不指定分区,分区的数量跟total-executor-cores指定几个核有关
分区数量决定了任务的并行度   并行度越高task越多
rdd.collect 提交结果  就会触发一次action   触发一次action就会有一个job
也可以在创建RDD的时候指定分区
val rdd = sc.parallelize(arr,100)      这边在第二个参数指定了有100个分区
也可以用makeRDD的方式创建RDD,当然也可以指定分区
val rdd = sc.parallelice(arr)

怎么进行分区的嫩
先totalSize(所有文件大小都加在一起)goalSize(指定的话就是1)不指定就是传进来的默认值2,所以如果不是指定的goalSize就是totalSize除以2,如果这个文件除以goalSize大于1.1就切
之前的对数据的map,split操作路径都可以不存在因为并没有真正的对数据进行操作,reduceBykey要路径存在因为要聚合从别的机器上抓取数据,sortBy会生成一个job,因为他要排序就要这道数据的信息,对数据进行采样构建一个新的特殊的分区器
记住:对RDD进行操作就是对RDD里的每一个分区进行操作,对RDD进行map本质上就是对RDD里每一个分区对应的迭代器进行map

RDD的算子的分类

Transformation: 即转换算子,调用转换算子会生成一个新的RDD, Transformation是 Lazy 的,不会触发job执行
Action: 行动算子,调用行动算子会触发job执行, 本质上是调用了sc.runJob方法, 该方法从最后一个RDD,根据
其依赖关系,从后往前,划分Stage,生成一个TaskSet
1.创建RDD的方法
(1)通过并行化方式,将Dirver端的集合转成RDD ,可以指定分区的数量

 val rdd: RDD[Int] = sc.parallelize(arr) 
 rdd.partitions.length   //查看分区数量

(2)从HDFS指定目录创建RDD ,也可以指定分区的数量

val lines: RDD[String] = sc.textFile("hdfs://linux01:8020/log")
rdd.partitions.length   //查看分区数量     

(2.1)从hdfs中读取数据分区数量 : 默认情况下 目录文件下所有文件的 totalSize(文件总大小)/NumSplits (切片数量) 得到 goalSize()
使用 文件大小/goalSize 结果大于1.1 就分为多个切片.
如果想要几个block块(文件)就有几个分区, 在创建rdd时指定计算goalSize的除数为1就可以了
val rdd1 = sc.textFile(“hdfs://linux01:8020/wc”,1)

DAG 有向无环图

srage
任务执行阶段
一个Stage对应一个TaskSet
一个TaskSet中的Task的数量取决于Stage中最后一个RDD分区的数量

dependency
依赖关系,指的是父RDD和子RDD之间的依赖关系
窄依赖:没有shfuffle产生,多个算子会被合并到一个Task中,即在一个pipeline中
宽依赖:有shuffle产生,是划分Stage的依据

JOB
触发一次Action形成一个完整的DAG,一个DAG对应一个job

一个job中有一到多个Stage,一个Stage对应一个TaskSet,一个TaskSet中有一到多个Task

Application
使用SparkSubmit提交的任务

一个Application中可以触发一到多次Action,触发一次Action形成一个DAG,一个DAG对应一个job

一个Appalication中可以有一到多个job

HashPartitioner源码分析

package cn.doit.spark.day02
/*
    HashPartitioner(分区器)源码分析
 */
object HashPartitionerDemo {
 
  def nonNegativeMod(x:Int , mod:Int): Int = {
    //文件的hashcode值为x,mod为下游分区
    val rawMod = x % mod
    rawMod + (if (rawMod < 0) mod else 0)
  }
 
  def main(args: Array[String]): Unit = {
 
//    val word = "spark"   //1
//    val word = "hadoop"  //1
    val word = "flume"     //3
 
    //index分到第几个区                4为下游分区
    val index = nonNegativeMod(word.hashCode, 4)
    println(index)
  }
}

RDD算子之Transformation 转换算子, lazy的,调用后生成一个新的RDD

  1. 不产生Shuffle的

map filter

map filter

package org.apache.spark.day02
 
import org.apache.spark.rdd.MapPartitionsRDD
import org.apache.spark.{SparkConf, SparkContext}
/*
  map   filter
     对RDD进行操作,其实是对RDD中每个分区对应的迭代器进行操作,迭代器会对每一个分区内的数据进行操作
 */
object MapPartitionsRDDDemo {
  def main(args: Array[String]): Unit = {
 
    //setMaster设置为本地模式
    val conf = new SparkConf().setAppName("MapDemo").setMaster("local[*]")
 
    //创建SparkContext
    val sc: SparkContext = new SparkContext(conf)
 
    //数组
    val arr = Array(1,2,3,4,5,6,7,8,9)
 
    //创建RDD 指定分区
    val rdd1 = sc.parallelize(arr, 3)
 
    //map 对RDD进行操作,其实是对RDD中的每个分区进行操作,是对RDD中每个分区对应的迭代器进行操作
//    val rdd2 = rdd1.map(_ * 10)
 
//    MapPartitionsRDD有包访问权限(private(this)),无法调用,可以伪装,
//    创建一个和路径和倒包一样的在里面就可以使用  org.apache.spark.day02.MapPartitionsRDDDemo
//        val func = (a:Int) => a * 10
//        val rdd2 = new MapPartitionsRDD[Int, Int](rdd1, (taskContext, index, iterator) => iterator.map(func))
 
    //filter
    val filtered = (a:Int) => a % 2 == 0
    val rdd2 = new MapPartitionsRDD[Int, Int](rdd1, (_, _, iterator) => iterator.filter(filtered))
 
    //将元素收集到一个数组中 打印  collect是Action算子
    println(rdd2.collect().toBuffer)
 
    //释放资源
    sc.stop()
  }
}

2 产生Shuffle的 在分布式计算中,将数据按照一定的计算逻辑(分区器),将具有相同规律的数据通过网络传输到指定的位置

reduceByKey groupByKey groupBy

reduceByKey

调用reduceByKey底层调用的是ShuffledRDD 特殊情况:调用reduceByKey不一定进行shuffle,如果前面的数据已经分好区了,

那就不需要在进行分区了(使用同样的分区器(列如都是Hashpartitioner),并且分区数量相同)

package cn.doit.spark.day02.demo02
 
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/*
  reduceByKey 有shuffle
 */
object ReduceByKeyDemo {
  def main(args: Array[String]): Unit = {
 
    val conf = new SparkConf().setAppName("ReduceByKeyDemo").setMaster("local[*]")
 
    val sc: SparkContext = new SparkContext(conf)
 
    val lines: RDD[String] = sc.textFile(args(0))
 
    val wordAndOne: RDD[(String, Int)] = lines.flatMap(_.split(" ")).map((_, 1))
 
//    val reduced: RDD[(String, Int)] = wordAndOne.reduceByKey(_ + _)
 
    //第一个函数:该分区内第一次出现key对应的value进行运算
    val f1 = (x:Int) => x
    //第二个函数:在该分区内将key相同的value继续进行局部聚合
    val f2 = (x:Int , y:Int) => x + y
    //第三个函数全局聚合
    val f3 = (a:Int , b:Int) => a + b
    //reduceByKey底层调用的就是combineByKey,再底层就是调用的ShuffledRDD
    //底层调用的是combineByKeyWithClassTag, 并在方法中中new的ShuffleRDD
    val reduced = wordAndOne.combineByKey(f1, f2, f3)
 
  //仅分区不排序,可以选择性的要不要排序
  //val shuffledRDD: ShuffledRDD[String, Int, Int] = new ShuffledRDD[String, Int, Int] 
  //(wordAndOne, new HashPartitioner(wordAndOne.partitions.length))
  // true是局部聚合,false是不局部聚合
  //shuffledRDD.setMapSideCombine(true)
  //聚合器,将三个参数聚合
  //val aggregator = new Aggregator[String, Int, Int](f1, f2, f3)
  //shuffledRDD.setAggregator(aggregator)
  //val res = shuffledRDD.collect()
  //println(res.toBuffer)
 
    //将数据放进一个数组
    val res = reduced.collect()
 
    //打印
    println(res.toBuffer)
  }
}
groupByKey(分组)  底层也是先局部再全局  效率高于groupBy 推荐使用

package cn.doit.spark.day02.demo03
 
import org.apache.spark.rdd.{PairRDDFunctions, RDD, ShuffledRDD}
import org.apache.spark.{Aggregator, HashPartitioner, SparkConf, SparkContext}
 
import scala.collection.mutable.ArrayBuffer
 
object GroupByKeyDemo {
  def main(args: Array[String]): Unit = {
 
    val conf = new SparkConf().setAppName("GroupByKeyDemo").setMaster("local[*]")
 
    val sc: SparkContext = new SparkContext(conf)
 
    val lines: RDD[String] = sc.textFile(args(0))
 
    val wordAndOne: RDD[(String, Int)] = lines.flatMap(_.split(" ")).map((_, 1))
 
//    val grouped = wordAndOne.groupByKey()
 
//    val grouped = new PairRDDFunctions[String, Int](wordAndOne).groupByKey()
 
    //第一个参数, 把第一个key对应的value装进可变数组中
    val f1 = (a:Int) => ArrayBuffer[Int](a)
    //第二个参数,局部分组,将相同key分到一组,对应的value放进ArrayBuffer中
    val f2 = (b:ArrayBuffer[Int], c:Int) => b += c
    //第三个参数,全局分组,将每个分区的结果分组,key相同的分到一组 ,将数组合并到一起
    val f3 = (x:ArrayBuffer[Int], y:ArrayBuffer[Int]) => x ++= y
    val shuffledRdd: ShuffledRDD[String, Int, ArrayBuffer[Int]] = new ShuffledRDD[String, Int, ArrayBuffer[Int]](wordAndOne, new HashPartitioner(wordAndOne.partitions.length))
    shuffledRdd.setMapSideCombine(false)
    //聚合器
    shuffledRdd.setAggregator(new Aggregator[String, Int, ArrayBuffer[Int]](f1,f2,f3))
    val grouped = shuffledRdd.collect()
    println(grouped.toBuffer)
 
//    val tuples = grouped.collect()
 
//    println(tuples.toBuffer)
  }
}

groupBy(分组) 比groupByKey更加灵活,但是效率比groupByKey慢一点,推荐使用groupByKey

package cn.doit.spark.day02.demo03
 
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
 
object GroupBy {
  def main(args: Array[String]): Unit = {
 
    System.setProperty("HADOOP_USER_NAME", "root")
    //设置为本地模式,setMaster
    val conf = new SparkConf().setAppName("GroupBy").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
 
    val lst: Seq[String] = List("江苏省,徐州市,2000", "浙江省,宁波市,2005", "山东省,青岛市,3000", "江苏省,南京市,2010","江苏省,徐州市,2000")
 
    val rdd1: RDD[String] = sc.parallelize(lst)
 
 
    //groupBy底层也是调用的groupByKey  更加灵活,但是效率低于groupByKey
//    val rdd2: RDD[(String, String, Double)] = rdd1.map(e => {
//      val fields = e.split(",")
//      (fields(0), fields(1), fields(2).toDouble)
//    })
//
//    val rdd3: RDD[(String, Iterable[(String, String, Double)])] = rdd2.groupBy(t => t._1)
//    val tuples = rdd3.collect()
//    println(tuples.toBuffer)
 
 
    //groupByKey效率高于groupBy, 推荐使用
    val rdd2: RDD[(String, (String, Double))] = rdd1.map(e => {
      val fields = e.split(",")
      (fields(0), (fields(1), fields(2).toDouble))
    })
    val rdd3: RDD[(String, Iterable[(String, Double)])] = rdd2.groupByKey()
    val result = rdd3.collect()
    println(result.toBuffer)
  }
}

distinct(过滤) 局部去重再全局去重

package cn.doit.spark.day02.demo04
 
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
 
object distinctDemo {
  def main(args: Array[String]): Unit = {
 
    val conf = new SparkConf().setAppName("distinctDemo").setMaster("local[*]")
 
    val sc = new SparkContext(conf)
 
    val arr = Array(1,2,3,4,5,1,2,3,4,5,6,7,8,7)
 
    val rdd1: RDD[Int] = sc.parallelize(arr, 2)
 
    val result = rdd1.distinct()
    println(result.collect().toBuffer)
 
//    //将数据编成k,v
//    val numAndNull: RDD[(Int,Null)] = rdd1.map(e =>(e,null))
//
//    //去重
//    val reduced = numAndNull.reduceByKey((a, _) => a)
//
//    //整理
//    val result = reduced.map(_._1)
//
//    println(result.collect().toBuffer)
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spark RDD(弹性分布式数据集)是Spark中最基本的数据结构之一,它是一个不可变的分布式对象集合,可以在集群中进行并行处理。RDD可以从Hadoop文件系统中读取数据,也可以从内存中的数据集创建。RDD支持两种类型的操作:转换操作和行动操作。转换操作是指对RDD进行转换,生成一个新的RDD,而行动操作是指对RDD进行计算并返回结果。RDD具有容错性,因为它们可以在节点之间进行复制,以便在节点故障时恢复数据。 Spark RDD的特点包括: 1. 分布式:RDD可以在集群中进行并行处理,可以在多个节点上进行计算。 2. 不可变性:RDD是不可变的,一旦创建就不能修改,只能通过转换操作生成新的RDD。 3. 容错性:RDD具有容错性,因为它们可以在节点之间进行复制,以便在节点故障时恢复数据。 4. 惰性计算:RDD的计算是惰性的,只有在行动操作时才会进行计算。 5. 缓存:RDD可以缓存到内存中,以便在后续操作中快速访问。 Spark RDD的转换操作包括: 1. map:对RDD中的每个元素应用一个函数,生成一个新的RDD。 2. filter:对RDD中的每个元素应用一个函数,返回一个布尔值,将返回值为true的元素生成一个新的RDD。 3. flatMap:对RDD中的每个元素应用一个函数,生成一个新的RDD,该函数返回一个序列,将所有序列中的元素合并成一个新的RDD。 4. groupByKey:将RDD中的元素按照key进行分组,生成一个新的RDD。 5. reduceByKey:将RDD中的元素按照key进行分组,并对每个分组中的元素进行reduce操作,生成一个新的RDD。 Spark RDD的行动操作包括: 1. count:返回RDD中元素的个数。 2. collect:将RDD中的所有元素收集到一个数组中。 3. reduce:对RDD中的所有元素进行reduce操作,返回一个结果。 4. foreach:对RDD中的每个元素应用一个函数。 5. saveAsTextFile:将RDD中的元素保存到文本文件中。 以上就是Spark RDD的详细介绍。 ### 回答2: Apache Spark是一款基于内存的分布式计算系统,可以处理大规模数据,其中最为重要的就是Spark中的RDD(Resilient Distributed Datasets,弹性分布式数据集),RDD是Spark中的基本数据结构,是一种类似于数组的分布式数据集,可以被分割成多个分区,并在集群中的多个节点间进行并行计算。RDD是Spark提高执行效率和数据可靠性的重要手段。 在Spark中,RDD具有以下三个特点:弹性、不可变和可分区。弹性指RDD能够自动进行数据分区和容错,即使节点出现故障,也能够自动从故障的节点中复制数据,提高了数据的可靠性和并行计算的效率。不可变指RDD一旦创建就不能够被改变,可以进行转换操作生成新的RDD,也可以被缓存到内存中以供重复使用。可分区则指RDD中可以被分成多个分区,实现并行计算。 Spark中RDD的API提供了丰富的操作方法,常见的操作包括:转换操作和动作操作。转换操作指对RDD进行转换操作,返回一个新的RDD对象,例如map()、filter()等;动作操作指对RDD进行计算并返回结果,例如reduce()、collect()等。 值得注意的是,RDD是一种惰性求值的数据结构,即当对RDD进行转换操作时并不会立即进行计算,而是当需要对RDD进行动作操作时才会进行计算,这种惰性求值的机制可以进一步提高Spark的效率。同时,为了提高计算效率,可以使用RDD的持久化(缓存)功能,将RDD持久化到内存中,以便复用。 总之,RDD是Spark中的核心数据结构,其弹性、不可变和可分区的特点以及丰富的API操作方法,为Spark实现高效计算和数据处理提供了重要的支持。 ### 回答3: Spark RDD是Spark的核心抽象,代表分布式的元素集合,支持多种操作和转换。RDD可以看作是一个不可变的分布式内存数据集合,由一些分布式的partition(分区)组成。 1. RDD的特性: - 分布式的数据集,可以跨越多个节点进行计算 - 可以并行处理,充分利用集群计算资源 - 不可变的数据集,任何对数据集的操作都会生成新的数据集 - 支持多种类型的转换操作,如map、filter、reduce、groupByKey等 2. RDD的创建: - 通过外部数据源创建RDD:从HDFS或其他存储系统中读取数据创建 - 通过程序中的数据结构创建RDD:从内存中的数据结构中创建 - 通过其他RDD转换创建RDD:通过对已有的RDD进行转换操作创建 3. RDD的转换: RDD支持多种类型的操作和转换,如map、filter、reduce、groupByKey等。这些转换操作不会立即执行,而是记录下来,等到需要输出结果时才会真正执行。 4. RDD的行动: 行动操作是指对RDD进行计算并返回结果的操作,如count、collect等。行动操作会立即触发RDD的计算过程。 5. RDD的缓存: RDD支持缓存操作,将一个RDD的结果缓存在内存中,提高后续对该RDD的计算效率。缓存可以在计算过程中多次使用,通过unpersist清理缓存。 6. RDD的持久化: 当RDD的计算过程非常复杂时,可以将计算过程中得到的RDD进行持久化以便后续使用。持久化可以选择将RDD保存在磁盘中或者内存中,也可以将RDD复制到多个节点上以保障数据的可靠性。 7. RDD的checkpoint: RDD的checkpoint是指将RDD的计算结果保存在HDFS或其他分布式存储系统中,以便后续查询和还原数据集。在计算复杂的RDD时,使用checkpoint可以避免计算过程中数据丢失的问题。 总的来说,Spark RDD是Spark分布式计算的核心特性,其提供对大规模数据集的分布式处理能力,以及丰富的操作和转换方式,使得程序员可以轻松地处理海量数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值