sparkRDD

本文深入探讨了Spark中的核心概念——弹性分布式数据集(RDD),详细解释了RDD的特点、依赖关系及其实现机制,包括缓存、检查点设置以及任务执行流程等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RDD

RDD弹性分布式数据集,spark最基本的数据抽象,代表一个不可变,可分区,里面元素可并行计算的集合。
具有数据流模型的特点:自动容错,位置感知性调度和可伸缩性。
RDD允许用户在执行多个查询时,显示地将工作集缓存在内存中,后续的查询能重用工作集,这极大提高查询速度
特点:一系列的分区,每一个函数作用于每个分区,RDD之间是一系列依赖,如果是k-v类型的RDD,会有一个分区器,分区器就是决定把数据放到哪个分区

一般Worker需要和DataNode节点部署在一起,这样可以有效的避免大量网络IO
一个分区一定在一个节点上,一个节点可以有多个分区
RDD由多个分区组成的分布式数据集
一个block块对应一个分区

启动spark-shell

./spark-shell 
--master spark://hadoop01:7077  master地址
--executor-memory 512m    内存
--total-executor-cores 2    cpu核数

提交任务

./spark-submit
.
--class org.apache... 类名
--master spark://hadoop01:7077  master地址
--executor-memory 512m    内存
--total-executor-cores 2    cpu核数
jar包路径
args 其它参数(需要传才写)

spark集群的启动流程

1、首先启动master进程
2、master开始解析slaves配置文件,找到worker的host,然后启动相应的worker
3、worker开始与master进行注册,把注册信息发送给master
4、master收到注册信息后,把注册信息保存到内存与硬盘中,然后master给worker发送注册成功的信息(masterurl)
5、worker收到master的url信息,开始与master建立心跳

spark集群提交流程

1、driver 端 的sparksubmit 进程与master进行通信,创建一个重要的对象(sparkcontext)
2、master收到任务信息后,开始资源调度,和所有的worker进行通信,找到比较空闲的worker,并通知worker启动excutor进程
3、executor进程启动后,开始与driver(client) 进行通信(反向注册),driver开始将任务提交到相应的executor,executor开始计算任务

RDD和它依赖的父RDD的依赖关系,窄依赖narrow dependency,宽依赖wide dependency

主要说的是partition  分区
窄依赖:一父分区只能对应一子分区        分组后join
宽依赖:一父分区可以对应多子分区,宽依赖与shuffle密不可分  没分组join

Lineage

RDD转换为RDD(只支持粗粒度转换),将创建RDD的一系列Lineage(即血统)记录下来,以便恢复分区
Lineage会记录元数据信息和转换行为,当RDD部分分区数据丢失时,通过Lineage保存的信息来重新运算和恢复丢失的数据分区

一系列一对一关系(窄依赖),会形成一个Pipeline。可以通过父分区来再运行计算丢失分区的数据,其它的Pipeline不受影响 

Lineage在开发中不如cache 然后checkpoint(缓存放到hdfs)

丢失分区数据后找回数据顺序:cache(内存中找),checkpoint(hdfs中恢复),Lineage(从父分区再运行计算)

它与集群容错机制有点像。
集群容错机制:当一个节点(Worker)突然宕机,Master(重新调度任务)会将宕机的节点Worker未完成的数据交给其它节点。其它节点再在自己上面启动一个Worker,然后Worker启动Executor运行

RDD 缓存 checkpoint

cache:重要RDD的缓存起来,里面调用的是persist,默认是内存缓存(可以选择缓存级别)
checkpoint:持久化到本地或者hdfs
Spark速度快的原因:在不同操作中可以在内存中持久化或者缓存多个数据集(RDD),当持久化后,每个分区都把计算的数据分片保存在内存中,在此RDD或者之后的计算中可以重用,使后续动作更加迅速。缓存是spark构建迭代式算法和快速交互式查询的关键。

为什么要checkpoint?
运行处的中间结果,往往很重要,所以为了保证数据的安全性,要把数据做检查点
最好把checkpoint 到RDFS,便于该集群所有节点访问到
在checkpoint之前,最好先cache一下,就是先把数据缓存到内存,这样便于运行程序时调用,也便于checkpoint时直接重缓存中获取数据

什么时候做checkpoint?
在发生shuffle之后做checkpoint,(shuffle速度慢,项目中shuffle越少越好)
checkpoint的步骤?
1、创建checkpoint储存目录  sc.checkpointDir("hdfs://...")
2、把数据cache起来  rdd.cache()
3、checkpoint   rdd.checkpoint()
### 注意:当数据很大时,慎用cache,也不要不用,(关键地方,数据集不是非常大就用)

rdd.unpersist() 清除cache(cache的rdd调用unpersist())

DAG 有向无环图 stage

stage划分:找到最后的RDD,向前找,以宽依赖划分(宽依赖前的)为一个stage,整体划为一个stage,直到所有RDD划分完。(每个宽依赖划)
Stage:根据RDD之间的依赖关系的不同将DAG划分为不同的Stage,对于窄依赖,partition的转换处理在stage中完成计算,对于宽依赖,由于有shuffle的存在,只能在parentRDD中处理完成后才开始接下来的计算,因此宽依赖是划分stage的依据。
划分stage是为了把RDD来生成一个个task提交到Executor中执行,所以需要把RDD 先划分stage再生成task。

一个Stage 生成n个分区个task

task的生成是依据stage,在stage中先划分pipeline(与分区个数相同),然后根据pipeline生成task
shuffle read :发生在shuffle后,把父RDD 的数据读取到子RDD中
shuffle write: 把中间结果数据写到磁盘,为了保证数据安全性。
shuffle write 为什么不写内存里?
1、避免结果集太大而占用太多的内存资源,造成内存溢出
2、保存到磁盘可以保证数据的安全性

任务执行流程 4个阶段

RDD的生成  :  RDD 依赖关系,
stage的划分  : DAG 划分stage(在sparkcontext中 调用了 DAGScheduler(划分stage的))
任务的生成  :在sparkcontext中 调用了 TaskSetScheduler
任务的提交  :多节点提交
在sparkcontext中  调用 actorSystem,DAGScheduler,TaskSetScheduler详情看sparkcontext源码

yarn提交任务和spark提交任务的对比

resoucemanager 相当于master,负责任务调度,nodemanage相当于worker,负责创建容器和启动自己的子进程。
client端相当于driver,提交任务
yarnchild 相当于executor,直接参与计算进程

JdbcRDD RDD与mysql的交互

//这是向mysql中数据库 bigdata下的location_info表中查询数据
object JdbcRDD {
  def main(args: Array[String]): Unit = {
    val conf =new SparkConf().setAppName("jdbcRDD").setMaster("local[*]")
    val sc =new SparkContext(conf)
    val conn=()=>{
      Class.forName("com.mysql.jdbc.Driver").newInstance()
      DriverManager.getConnection("jdbc:mysql://192.168.216.53:3306/bigdata?useUnicode=true&characterEncoding=utf8","root","root")
    }
    val sql="select id,location,counts,access_date from location_info where id >= ? and id <= ? order by counts"
    val jdbcRDD =new JdbcRDD(
      sc,conn,sql,0,100,2,
      res=>{
        val id=res.getInt("id")
        val location =res.getString("location")
        val counts=res.getInt("counts")
        val access_date=res.getDate("access_date")
        (id,location,counts,access_date)
      }
    )
    println(jdbcRDD.collect().toBuffer)
    sc.stop()
  }
}

自定义排序 比较两个或多个字段时

第一种有隐式转换,第二种就直接继承

object Mysort {
  implicit val girlOrdering = new Ordering[Girl] {
    override def compare(x: Girl, y: Girl) = {
      if (x.faceValue != y.faceValue) {
        x.faceValue - y.faceValue
      } else {
        y.age - x.age
      }
    }
  }
}
//case class Girl(val faceValue: Int, val age: Int) {}//第一种
case class Girl(val faceValue: Int, val age: Int) extends Ordered[Girl] {
  override def compare(that: Girl) = {
    if (this.faceValue != that.faceValue) {
      this.faceValue - that.faceValue
    } else {
      this.age - that.age
    }
  }
}
object CustimSort {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("jdbcRDD").setMaster("local[*]")
    val sc = new SparkContext(conf)
    val girlInfo = sc.parallelize(Array(("a", 80, 25), ("b", 84, 24), ("c", 85, 26)))
    //第一种排序方式
    //    import Mysort.girlOrdering
    //    val res: RDD[(String, Int, Int)] = girlInfo.sortBy(x=>Girl(x._2,x._3),false)
    //第二种
    val res: RDD[(String, Int, Int)] = girlInfo.sortBy(x => Girl(x._2, x._3), false)
    println(res.collect().toBuffer)
    sc.stop()
  }
}

累加器 Accumulator

task 只能对accumulator进行累加

spark提供的accumulator,主要用于多个节点对一个变量进行共享操作。
它提供了多个task对一个变量并行的操作功能,task只能对Accumutor进行累加的操作,不能读取其值
只有driver才能读取。
/**
  * 累加器,就是累加操作
  */
object Accumulator {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("jdbcRDD").setMaster("local[*]")
    val sc = new SparkContext(conf)
    val sum: Accumulator[Int] = sc.accumulator(0)
    val numbers = sc.parallelize(Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 0), 2)
    numbers.foreach(num => {
      sum += num
    })
    println(sum)
    sc.stop()
  }
}
<think>好的,用户想了解Spark RDD的使用和概念。首先我需要回顾一下RDD的基本定义和核心特性。根据引用[3],RDD是弹性分布式数据集的缩写,是Spark对不可变分布式数据集的抽象。这意味着RDD代表一个可以并行操作的元素集合,具有容错性。 接下来,我需要解释RDD的五大特性,包括分区、依赖关系、计算函数、分区器以及优先位置。这些都是RDD的核心概念,用户需要清楚这些特性如何支持容错和高效计算。 然后要讲RDD的创建方式。常见的有从外部数据源加载,比如HDFS或本地文件系统,或者通过转换已有的RDD生成新的RDD。需要举一些例子,比如parallelize方法或者textFile方法。 关于RDD的操作,分为转换(Transformations)和动作(Actions)。转换是惰性的,只有触发动作时才会执行。这里可以举一些常用的转换和动作的例子,如map、filter、reduceByKey,以及collect、count等。同时要注意区分宽依赖和窄依赖,宽依赖会触发shuffle,影响性能。 容错机制方面,RDD通过血统(Lineage)来恢复丢失的分区。每个RDD记录了自己是如何从其他RDD转换而来的,这样在节点失败时可以根据这些信息重新计算丢失的数据,而不需要复制多份数据。 另外,用户可能对RDDSpark生态系统中的位置感兴趣。比如,DStream是Spark Streaming的基础,由一系列RDD组成,如引用[1]和[2]提到的。而结构化流(Structured Streaming)则是基于DataFrame,优化更多,但底层可能还是利用了RDD的特性。 需要提到RDD的适用场景,比如需要低延迟操作、精细控制数据分区,或者使用非结构化数据时。同时也要指出当需要更高级优化时,DataFrame或Dataset可能更合适,如引用[5]提到的转换器和预测器的例子,虽然那更多是ML库的内容,但能说明生态系统的多样性。 最后,生成的相关问题需要覆盖RDD与其他组件的比较,比如DStream和DataFrame,以及优化方法、容错机制和适用场景。这样用户如果有进一步的问题,可以继续深入。</think>### Spark RDD 核心概念与使用详解 #### 一、RDD 基础定义 **弹性分布式数据集(Resilient Distributed Dataset, RDD)** 是 Spark 的底层核心抽象,代表一个**不可变、分区的数据集合**,支持并行操作[^3]。其核心特性可总结为五大要素: - **分区列表**:数据被划分为多个分区(Partition),每个分区在集群节点上分布式存储 - **依赖关系**:记录父 RDD 到子 RDD 的转换关系(窄依赖/宽依赖) - **计算函数**:用于从父 RDD 计算当前分区的函数 - **分区器**:定义数据分布规则(如 HashPartitioner) - **优先位置列表**:标识数据分片的最佳计算位置(数据本地性优化) #### 二、RDD 创建方式 1. **从外部数据源加载**: ```python # 从本地文件创建 rdd = sc.textFile("hdfs://path/to/file.txt") # 从集合创建 rdd = sc.parallelize([1,2,3,4,5]) ``` 2. **通过转换操作生成**: ```python mapped_rdd = rdd.map(lambda x: x*2) filtered_rdd = rdd.filter(lambda x: x>3) ``` #### 三、核心操作类型 | 操作类型 | 特点 | 示例 | |---------|------|------| | **Transformations**(转换) | 惰性执行,生成新 RDD | `map()`, `filter()`, `groupByKey()` | | **Actions**(动作) | 触发实际计算 | `collect()`, `count()`, `saveAsTextFile()` | | **宽依赖操作** | 涉及数据重分布(Shuffle) | `join()`, `reduceByKey()` | #### 四、容错机制 通过**血统(Lineage)**实现容错: $$ \text{RDD}_n = f(\text{RDD}_{n-1}) = f_{n} \circ f_{n-1} \circ \cdots \circ f_1(\text{原始数据}) $$ 当某个分区丢失时,Spark 可根据这个依赖链重新计算该分区[^3]。 #### 五、应用模式示例 ```python # 词频统计经典案例 lines = sc.textFile("hdfs://...") words = lines.flatMap(lambda line: line.split(" ")) pairs = words.map(lambda word: (word, 1)) counts = pairs.reduceByKey(lambda a,b: a+b) counts.saveAsTextFile("hdfs://output") ``` #### 六、与流处理的关联 在 Spark Streaming 中,DStream 本质上是**时间维度上的 RDD 序列**: $$ \text{DStream} = \{ \text{RDD}_t \mid t \in \text{时间窗口} \} $$ 每个微批处理实际上是对 RDD 的操作[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值