大数据 -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 具有以下五个关键特征:
分区列表(Partitions):
RDD 由多个分区组成,每个分区包含数据集的一部分
分区是 Spark 并行处理的基本单位
例如:一个包含 100 万条数据的 RDD 可能被分成 10 个分区,每个分区约 10 万条数据
计算函数(Compute Function):
每个 RDD 都有一个计算函数 compute,用于对分区进行计算
这个函数定义了如何从父 RDD 计算出当前 RDD
例如:map 操作会对每个分区应用相同的转换函数
依赖关系(Dependencies):
RDD 保存了与其他 RDD 的依赖关系列表
依赖分为窄依赖(Narrow Dependency)和宽依赖(Wide Dependency)
窄依赖:每个父 RDD 分区最多被子 RDD 的一个分区依赖(如 map、filter)
宽依赖:每个父 RDD 分区被子 RDD 的多个分区依赖(如 groupByKey)
分区器(Partitioner)[可选]:
对于键值对 RDD,可以指定分区器(如 HashPartitioner 或 RangePartitioner)
分区器决定了数据在不同节点上的分布方式
例如:使用 HashPartitioner 会按照键的哈希值分配数据
优先位置列表(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 的转换操作可以构建多阶段处理流水线
典型流程:数据清洗 → 特征提取 → 模型训练
示例:
4. 需要基于键值对的聚合操作
提供 reduceByKey、groupByKey 等专门针对键值对 RDD 的操作
适合场景:统计计算、关联分析等
示例:
wordCount = text.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)
实际应用领域
ETL 处理(数据抽取-转换-加载):
处理大规模日志文件
数据格式转换(CSV→Parquet)
数据质量校验
机器学习算法:
支持迭代式计算(梯度下降等)
可以配合 MLlib 库使用
适合定制化算法实现
图计算:
通过 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)来解决上述问题:
持久化存储:
将 RDD 数据写入分布式文件系统如 HDFS
存储格式通常是二进制格式,保证高效读写
默认存储级别是 MEMORY_AND_DISK_SER
血缘关系切断:
恢复机制:
当 CheckPoint 过的 RDD 需要被使用时,直接从存储系统读取
不再需要沿血缘关系重新计算
3. 适用场景与最佳实践
典型应用场景:
机器学习模型训练(迭代算法)
图计算算法(如 PageRank)
流处理中的有状态计算
配置建议:
对于迭代算法,建议每 5-10 次迭代执行一次 CheckPoint
选择高性能的存储后端,如 Alluxio 可以加速 CheckPoint 过程
考虑使用 REPLICATE 存储级别提高可靠性
与 persist()的区别:
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 分区计算任务

版权声明: 本文为 InfoQ 作者【武子康】的原创文章。
原文链接:【http://xie.infoq.cn/article/32aed4471c83223bc4f9cf70e】。文章转载请联系作者。
评论