大数据-83 Spark RDD详解:特性、优势与典型应用场景

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布!"快的模型 + 深度思考模型 + 实时路由",持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年08月18日更新到: Java-100 深入浅出 MySQL事务隔离级别:读未提交、已提交、可重复读与串行化 MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈! 大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

章节内容

上节我们完成了如下的内容:

  • Hadoop 集群启动
  • Spark 集群启动
  • h121 h122 h123 节点启动
  • 集群启动测试 SparkShell

什么是RDD

RDD的基本概念

RDD(Resilient Distributed Dataset)是Spark框架中最核心的数据抽象,它是Spark实现分布式计算的基础数据结构。RDD代表一个不可变、可分区、元素可并行计算的分布式集合。

RDD的核心特性

RDD具有以下五个关键特征:

  1. 分区列表(Partitions)

    • RDD由多个分区组成,每个分区包含数据集的一部分
    • 分区是Spark并行处理的基本单位
    • 例如:一个包含100万条数据的RDD可能被分成10个分区,每个分区约10万条数据
  2. 计算函数(Compute Function)

    • 每个RDD都有一个计算函数compute,用于对分区进行计算
    • 这个函数定义了如何从父RDD计算出当前RDD
    • 例如:map操作会对每个分区应用相同的转换函数
  3. 依赖关系(Dependencies)

    • RDD保存了与其他RDD的依赖关系列表
    • 依赖分为窄依赖(Narrow Dependency)和宽依赖(Wide Dependency)
    • 窄依赖:每个父RDD分区最多被子RDD的一个分区依赖(如map、filter)
    • 宽依赖:每个父RDD分区被子RDD的多个分区依赖(如groupByKey)
  4. 分区器(Partitioner)[可选]

    • 对于键值对RDD,可以指定分区器(如HashPartitioner或RangePartitioner)
    • 分区器决定了数据在不同节点上的分布方式
    • 例如:使用HashPartitioner会按照键的哈希值分配数据
  5. 优先位置列表(Preferred Locations)[可选]

    • 每个分区有一组优先位置(节点)列表
    • 基于数据本地性原则,Spark会尽量将计算任务调度到数据所在节点
    • 例如:从HDFS读取的数据会记录其所在的数据节点位置

RDD的设计优势

RDD的设计带来了以下重要优势:

  • 容错性:通过血缘关系(Lineage)可以重建丢失的分区
  • 并行性:分区机制天然支持并行计算
  • 内存计算:数据可以持久化在内存中,提高计算速度
  • 惰性求值:转换操作(Transformation)延迟执行,直到遇到行动操作(Action)才真正计算

RDD的应用场景详解

RDD(弹性分布式数据集)作为Spark的核心数据结构,适用于以下典型场景:

1. 需要多次使用的数据集(内存缓存优势)

  • 当同一个数据集需要被多个操作重复使用时,RDD可以通过.persist().cache()方法将数据缓存在内存中
  • 典型应用:迭代式机器学习算法(如K-means聚类),每次迭代都需要重复使用训练数据
  • 示例:val cachedRDD = sc.textFile("data.txt").cache()

2. 需要细粒度控制的转换操作

  • RDD提供丰富的底层API,支持完全控制数据处理过程
  • 包括:map、filter、flatMap等基本转换,以及自定义分区控制
  • 适合场景:需要精确控制数据分区或执行位置的复杂计算任务

3. 需要构建复杂的数据处理流水线

  • 通过RDD的转换操作可以构建多阶段处理流水线
  • 典型流程:数据清洗 → 特征提取 → 模型训练
  • 示例:
scala 复制代码
val pipeline = sc.textFile("logs")
  .filter(_.contains("ERROR"))  // 过滤
  .map(parseLog)                // 解析
  .groupBy(_.service)           // 分组

4. 需要基于键值对的聚合操作

  • 提供reduceByKey、groupByKey等专门针对键值对RDD的操作
  • 适合场景:统计计算、关联分析等
  • 示例:wordCount = text.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

实际应用领域

  1. ETL处理(数据抽取-转换-加载):

    • 处理大规模日志文件
    • 数据格式转换(CSV→Parquet)
    • 数据质量校验
  2. 机器学习算法:

    • 支持迭代式计算(梯度下降等)
    • 可以配合MLlib库使用
    • 适合定制化算法实现
  3. 图计算:

    • 通过GraphX扩展实现
    • 用于社交网络分析、推荐系统等
    • 支持Pregel图计算模型

与其他API的关系

虽然Spark后续推出了DataFrame(结构化API)和DataSet(类型安全API),但RDD仍保持其独特优势:

  • 最底层的执行引擎
  • 完全控制计算过程
  • 支持任意Java/Scala对象
  • 适合非结构化数据处理

在Spark 2.x+版本中,RDD可以与DataFrame相互转换,开发者可以根据场景选择最合适的抽象层。

RDD 特点介绍

不可变性(Immutability)

RDD一旦创建,就不能被修改。每次对RDD进行操作(例如过滤、映射等)都会产生一个新的RDD。这种不可变性简化了并行处理,因为无需担心多个计算节点间的数据竞争。

分布式(Distributed)

RDD的数据分布在多个节点上,这使得Spark能够处理大规模的数据集。RDD的每个分区都可以在不同的节点上独立处理。

容错性(Fault Tolerance)

RDD通过"血统"(Lineage)记录其生成方式。如果RDD的某些分区在计算过程中丢失,可以根据这些血统信息重新计算丢失的数据。通过这种方式,RDD能够在节点故障时自动恢复。

惰性求值(Lazy Evaluation)

RDD的操作被分为两类:转换操作(Transformations) 和 行动操作(Actions)。转换操作是惰性求值的,即不会立即执行,而是等到遇到行动操作时才触发计算。这样做的好处是可以通过合并多个转换操作来优化计算过程,减少不必要的中间计算。

类型安全(Type Safety)

在Scala语言中,RDD是类型安全的,意味着你可以在编译时捕获类型错误,这对开发者来说非常有帮助。

并行操作(Parallel Operation)

RDD的每个分区可以独立进行处理,允许多线程或多节点并行执行,充分利用集群的计算资源。

缓存与持久化(Caching and Persistence)

可以将RDD缓存或持久化到内存或磁盘中,以便在多次使用时避免重复计算,从而提高性能。

丰富的API

RDD提供了丰富的API支持各种操作,包括map、filter、reduceByKey、groupBy、join等,能够满足大部分分布式数据处理的需求。

RDD的特点

分区

RDD逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候通过一个compute函数得到每个分区的数据。如果RDD是通过己有的文件系统构建,则compute函数是读取指定文件系统中的数据,如果RDD是通过其他RDD转换而来,则compute函数是执行转换逻辑将其他RDD的数据进行转换。

只读

RDD是只读的,要想改变RDD中的数据,只能在现有的RDD基础上创建新的RDD。 一个RDD转换为另一个RDD,通过丰富的算子(map filter union join reduceByKey等等)实现,不再像MR那样写Map和Reduce了。

RDD的操作算子包括两类:

  • Transformation:用来对RDD进行转化,延迟执行(Lazy)
  • Action:用来出发RDD的计算,得到相关计算结果或者将RDD保存的文件系统中

依赖

RDDs通过操作算子进行转换,转换得到的新RDD包含了从其他RDDs衍生出所必须得信息,RDDs之间维护着这种学院关系(lineage),也称为依赖。

  • 窄依赖:RDDs之间的分区是一一对应的(1对1 或者 n对1)
  • 宽依赖:子RDD每个分区与父RDD的每个分区都有关,是多对多的关系

缓存

可以控制存储级别(内存、磁盘等)来进行缓存 如果在应用程序中多次使用同一个RDD,可以将RDD缓存起来,该RDD只有在第一次计算的时候会根据血缘关系得到分区的数据,在后续其他地方用到该RDD的时候,会直接从缓存取而不用再根据血缘计算,加速后期的重用。

CheckPoint机制详解

1. RDD血缘关系的局限性

RDD(弹性分布式数据集)通过DAG(有向无环图)记录其转换过程,形成所谓的"血缘关系"(Lineage)。这种机制确实为RDD提供了天然的容错能力:当某个分区数据丢失时,Spark可以根据这个血缘关系重新计算恢复数据。然而,这种机制在长时间迭代型应用(如机器学习训练)中会暴露出明显的性能问题:

  • 血缘链增长问题:例如在一个迭代100次的机器学习算法中,每次迭代都会产生新的RDD,血缘关系会变得非常长。如果第99次迭代失败,需要从头开始重新计算所有前98次迭代的结果。
  • 计算资源浪费:长血缘链导致恢复时间显著增加,造成计算资源的浪费。

2. CheckPoint的工作原理

CheckPoint机制通过将RDD数据持久化到可靠存储系统(如HDFS)来解决上述问题:

  1. 持久化存储

    • 将RDD数据写入分布式文件系统如HDFS
    • 存储格式通常是二进制格式,保证高效读写
    • 默认存储级别是MEMORY_AND_DISK_SER
  2. 血缘关系切断

    scala 复制代码
    // 示例:设置CheckPoint目录并执行CheckPoint
    sc.setCheckpointDir("hdfs://...")
    val rdd = sc.parallelize(1 to 100).map(_ * 2)
    rdd.checkpoint()
  3. 恢复机制

    • 当CheckPoint过的RDD需要被使用时,直接从存储系统读取
    • 不再需要沿血缘关系重新计算

3. 适用场景与最佳实践

典型应用场景

  • 机器学习模型训练(迭代算法)
  • 图计算算法(如PageRank)
  • 流处理中的有状态计算

配置建议

  • 对于迭代算法,建议每5-10次迭代执行一次CheckPoint
  • 选择高性能的存储后端,如Alluxio可以加速CheckPoint过程
  • 考虑使用REPLICATE存储级别提高可靠性

与persist()的区别

特性 CheckPoint persist()
存储位置 可靠存储系统 内存/本地磁盘
血缘关系 完全切断 保留
可靠性 取决于存储级别
性能影响 较大(涉及IO) 较小

4. 实现细节

Spark执行CheckPoint的过程实际上是惰性的,只有在遇到Action操作时才会真正执行。为确保数据可靠性,Spark会先执行一次额外的计算任务将数据写入CheckPoint目录,这个过程是同步阻塞的。在Spark 2.1之后,引入了异步CheckPoint机制来减少性能影响。

Spark编程模型

  • RDD表示数据对象
  • 通过对象上的方法调用来对RDD进行转换
  • 最终显示结果或者将结果输出到外部数据源
  • RDD转换算子称为Transformation是Lazy的(延迟执行)
  • 只有遇到 Action算子,才会执行RDD的转换操作

如果要使用Spark,就需要编写Driver程序,它被提交到集群运行。

  • Driver中定义了一个或多个RDD,并调用RDD上的各种算子
  • Worker则执行RDD分区计算任务
相关推荐
Victor3561 小时前
Redis(37)Redis集群中的主节点和从节点是如何协作的?
后端
Victor3561 小时前
Redis(36)Redis集群的槽(Slot)是如何分配的?
后端
拾光师7 小时前
flume事务机制详解:保障数据可靠性的核心逻辑
大数据·flume
向上的车轮8 小时前
Odoo与Django 的区别是什么?
后端·python·django·odoo
科技小郑8 小时前
吱吱企业通讯软件可私有化部署,构建安全可控的通讯办公平台
大数据·网络·安全·信息与通信·吱吱企业通讯
拓端研究室8 小时前
Python电影票房预测模型研究——贝叶斯岭回归Ridge、决策树、Adaboost、KNN分析猫眼豆瓣数据
大数据
完美世界的一天9 小时前
Golang 面试题「中级」
开发语言·后端·面试·golang
小明说Java9 小时前
解密双十一电商优惠券批量下发设计与实现
后端
bobz9659 小时前
virtio-networking 5: 介绍 vDPA kernel framework
后端
橙子家10 小时前
接口 IResultFilter、IAsyncResultFilter 的简介和用法示例(.net)
后端