精选·Spark

RDD 是什么?它的血缘机制和容错原理怎么讲清楚?

91学AI·2026/7/13·8 阅读

考察点

这是 Spark 面试的开场题,答成「RDD 是一个分布式数据集」基本就凉了。面试官想听三层:RDD 的设计动机(为什么是不可变的、为什么是惰性求值)、血缘容错和传统副本容错的取舍、以及这套机制在什么场景会失效(血缘过长、迭代计算)。追问常往窄依赖/宽依赖、cache 与 checkpoint 的关系、DataFrame 时代 RDD 还有没有存在意义这些方向走。

参考答案

RDD 的本质

RDD(Resilient Distributed Dataset)是 Spark 的核心数据抽象。官方定义里有五个特性,每一个都值得展开:

  • 分区(partitioned):一个 RDD 被切成若干 partition,每个 partition 是一个计算任务的基本单位。并行度就是分区数。
  • 依赖(dependencies):RDD 知道自己由哪些父 RDD 经过什么转换得来,这就是血缘。
  • 计算函数(compute):对每个 partition 应用同一个函数得到结果,数据本地性允许的范围内 Spark 会把任务调度到数据所在节点。
  • 分区器(partitioner,可选):KV 型 RDD 可以携带 hash/range 分区信息,决定 shuffle 后数据的分布。
  • 优先位置(preferred locations,可选):比如从 HDFS 读数据时,每个 partition 对应 block 的位置,调度器据此做 locality 优化。

三个关键词要立住:不可变(RDD 一旦创建不能修改,转换产生新 RDD)、惰性(transformation 只记录不执行,遇到 action 才触发计算)、可重算(丢了就从血缘倒推重新算)。

为什么设计成这样

不可变和惰性是配套的。不可变意味着任何算子都不产生副作用,同一个 RDD 可以被多条分支复用而不用担心脏数据;惰性意味着 Spark 可以把一长串转换攒起来做整体优化(比如 filter 下推、合并 map 链),而不是来一个算子跑一遍。

对比一下 MapReduce:MR 每一步都要把中间结果落 HDFS,靠三副本保证容错,代价是大量磁盘 IO。Spark 的思路完全不同——中间结果不落盘,容错靠血缘重算。这是数学上的空间换时间反过来用:用 CPU 重算换取磁盘 IO 的节省。内存时代重算往往比读盘还快,这个赌注赌对了。

血缘容错的运作方式

每个 RDD 对象里存着它的 lineage:父 RDD 列表 + 转换函数。某个 partition 丢失(executor 挂了、内存数据被逐出)时,调度器沿着血缘往上找到还能用的最近一级 RDD,只重算丢失的那几个 partition,而不是整个数据集。

这里分两种情况,也就是窄依赖和宽依赖:

  • 窄依赖(map、filter、union):父 partition 和子 partition 一一对应或少对一。重算丢失的 partition 只需要它自己的父 partition,范围小、无 shuffle,恢复很快。
  • 宽依赖(groupByKey、join 未 co-partition):子 partition 依赖父 RDD 的全部或大量 partition。重算一个丢失的 partition 可能需要父 RDD 全量重算 + shuffle,代价大得多。

所以面试里要说清楚:血缘容错的开销和依赖类型强相关,这也是 stage 划分以 shuffle 为边界的根本原因。

血缘不是万能的

迭代计算(机器学习里的梯度下降、图计算 PageRank)下,RDD 链会越滚越长,血缘也跟着变长。两个问题随之而来:一是 driver 端血统链占内存、调度开销变大,二是链上任何一个 partition 丢了,重算要回溯几十上百代,代价不可接受。这就是 checkpoint 存在的意义——把某个 RDD 的物化结果写到可靠存储(HDFS),切断前面的血缘,从此重算只回溯到 checkpoint 点。注意 checkpoint 是 action,而且要先把 RDD cache 住再 checkpoint,否则 checkpoint 本身会触发一次额外计算。

可能的追问

  • 血缘容错和副本容错各自适合什么场景? 重算便宜、数据可重放的批处理场景血缘占优;重算代价高或数据不可再得(流数据、外部系统实时读取)时副本/预写日志更稳妥。Structured Streaming 用 WAL + checkpoint 就是这个权衡。
  • DataFrame/Dataset 比 RDD 好在哪里,RDD 什么时候还必须用? 前两者有 schema,能吃 Catalyst 优化和 Tungsten 的堆外内存、codegen;RDD 是 Java 对象,序列化开销大、无优化空间。但碰到非结构化数据、需要精细控制分区/迭代(自定义算子、图算法)时还是得落到 RDD API。
  • cache 会不会切断血缘? 不会。cache 只是把物化结果留在内存/磁盘,血缘还在;数据丢了仍按血缘重算,只是能命中缓存的祖先分区可以跳过重算。切断血缘的只有 checkpoint。

评论 (0)

暂无评论,快来抢沙发吧!

91学AI

© 2026 91学AI · 按岗位学 AI 与大数据. All rights reserved.