理解Spark RDD

1. 概念与属性

Q1: 什么是 RDD?它有哪些核心属性?

回答思路:

定义: RDD 是 Spark 的核心抽象,全称是弹性分布式数据集。它是只读的、分区的记录集合,能够自动从节点故障中恢复(容错)。

五大属性(硬核考点):

分区列表: 数据集被切分为多个分区,分布在集群的不同节点上。

计算函数: 每个分区都有一个函数去计算它( compute 方法)。

依赖关系: RDD 之间存在依赖(窄依赖/宽依赖),也就是血统(Lineage)。

分区器(可选): 针对 Key-Value 类型的 RDD,比如 HashPartitioner。

优先位置(可选): 数据本地性,比如 HDFS 块在哪里,计算就优先在哪里启动(移动计算而非数据)。

Q2: RDD 有什么优缺点?

优点: API 丰富、细粒度控制、类型安全(编译时检查)。

缺点(结合资料):

缺乏优化: 没有内置的优化引擎(不像 DataFrame 有 Catalyst 优化器)。

更新粒度粗: 不支持细粒度的写/更新操作(只能批量重写)。

不支持增量迭代: 相比 Flink,Spark RDD 更适合全量批处理。

2. 核心算子区别

Q3:  map  和  mapPartitions  的区别?

核心区别: 作用粒度不同。

map :处理单条数据。一条一条地处理,处理 N 条数据,函数执行 N 次。

mapPartitions :处理整个分区(Iterator)。一次接收一个分区的所有数据。如果分区有 1000 条数据,函数只执行 1 次。

优缺点:

mapPartitions  性能更高(例如写数据库时,可以复用连接,减少连接创建开销)。

mapPartitions  容易导致 OOM(内存溢出),因为一次性加载了整个分区的数据。

Q4:  reduceByKey  和  groupByKey  的区别?(Shuffle 优化经典题)

核心区别: 是否在 Map 端预聚合。

reduceByKey :会在 Map 端先进行 combine(预聚合),减少网络传输的数据量,性能高。

groupByKey :不会预聚合,直接把所有数据拉取过来,数据量大,性能低。

结论: 能用  reduceByKey  就不用  groupByKey 。

Q5:  repartition  和  coalesce  的区别?

核心区别: 是否强制 Shuffle。

repartition :底层调用  coalesce  且  shuffle=true 。一定会发生 Shuffle。通常用于增加分区数或彻底洗牌。

coalesce :可以指定  shuffle=false 。通常用于减少分区数且不希望发生 Shuffle 的场景(但如果是扩大分区,必须开启 Shuffle)。

Q6:  join  操作有什么优化经验?

大表小表 Join: 使用 Broadcast Join(Map-side Join)。将小表广播到每个节点,避免 Shuffle。

大表大表 Join: 如果发生数据倾斜,可以使用加盐(Salting)技术,给 Key 加上随机前缀打散数据。

3. 执行与容错原理

Q7: 什么是宽依赖和窄依赖?有什么区别?

窄依赖: 父 RDD 的一个分区只被子 RDD 的一个分区依赖(如  map ,  filter )。优点: 可以流水线执行,失败恢复快(只需要重算父 RDD 的对应分区)。

宽依赖: 父 RDD 的一个分区被子 RDD 的多个分区依赖(如  groupByKey ,  join )。特点: 必须发生 Shuffle,是 Stage 的边界。

Q8: RDD 是如何实现容错的?

基于 Lineage(血统): 如果 RDD 的某个分区丢失,Spark 可以通过记录的依赖关系(Lineage),重新从原始数据开始计算丢失的分区,而不需要复制数据。

CheckPoint(检查点): 将 RDD 写入可靠的存储(如 HDFS),切断血统链,防止血统链过长导致恢复慢。

Q9: 为什么要持久化(Persist/Cache)?

场景: 当一个 RDD 被多次使用时,如果不持久化,每次 Action 都会重新计算一遍血统,非常耗时。

区别:  cache()  是  persist()  的特例,默认存在内存( MEMORY_ONLY ); persist()  可以指定存磁盘、序列化存储等策略。

posted @ 2026-01-29 22:49  scales123  阅读(3)  评论(0)    收藏  举报