SparkStreaming 之 DStream 底层结构剖析

摘要:DStream 是 Spark Streaming 的核心抽象,但很多人用了很久也没搞清楚它到底是什么。这篇把 DStream 拆开------它本质是"RDD 的时间序列",本身不存数据;讲清它内部的 DStreamGraph、Lineage 血缘,以及 getOrCompute 怎么按时间点把 DStream 变成 RDD,顺带说清有状态操作为什么要强制开 checkpoint。

关键词:Spark Streaming, DStream, RDD 序列, DStreamGraph, getOrCompute, checkpoint


一、DStream 到底是个什么东西

先给一个判断,省得绕:DStream 是"RDD 的时间序列",它自己一个字节的数据都不存

回想上一篇文章里 batchInterval 的概念------Spark Streaming 每 2 秒(举例)切一个 batch,每个 batch 就是一个 RDD。DStream 就是把这些按时间排好的 RDD 串起来的抽象:

复制代码
RDD @ t0   RDD @ t1   RDD @ t2   ...   RDD @ tn
(batch 0)  (batch 1)  (batch 2)        (batch n)

所以你写的 ds.map(...)ds.reduceByKey(...) 这些操作,本质上是在描述"每个时间片的 RDD 该怎么变换",而不是立刻去算。这一点和 RDD 的惰性求值是一脉相承的------DStream 的转换操作构建的是依赖链,只有遇到 output 操作(printsaveAsTextFilesforeachRDD)才真正触发计算。


二、DStream 的内部结构

一个 DStream 内部有三块关键的东西。

2.1 DStreamGraph:DStream 的 DAG

和 RDD 有 DAG 一样,DStream 也有自己的图结构 DStreamGraph,里面按角色分三类节点:

  • InputDStream :数据入口,比如 SocketInputDStreamKafkaInputDStream,对应上一篇讲的 Receiver。
  • TransformedDStream :中间转换,map/flatMap/filter/join/window 都会产生这种节点。
  • ForEachDStream :输出节点,foreachRDDprint 属于这一类。

DStreamGraph 维护这张依赖图,每个 batch 时间点到了,就顺着图把每个 DStream 对应的 RDD 算出来。

2.2 Lineage:血缘

DStream 同样记录完整的血缘------"我是从哪个父 DStream、经过什么转换来的"。这条链子的用途和 RDD 的 lineage 一样:容错。

不过 DStream 的血缘有个 RDD 没有的要求:涉及有状态操作或需要故障恢复时,必须开 checkpoint。因为 DStream 的血缘是逻辑上的转换链,跨很多 batch 之后这条链会非常长,光靠血缘重算既不现实,也没法恢复"累计到现在的状态"。checkpoint 会把依赖链和状态持久化到可靠存储(HDFS),Driver 挂了能从 checkpoint 拉起来接着算。

scala 复制代码
ssc.checkpoint("hdfs://namenode:8020/checkpoint/streaming-app")

// 有状态操作,不开 checkpoint 会直接抛异常
val state = lines.updateStateByKey(updateFunc)

三、转换操作的两个分类维度

理解 DStream 的操作,可以按两个维度切,这直接影响性能和要不要 checkpoint。

维度一:无状态 vs 有状态

无状态 有状态
代表算子 map / filter / union / join updateStateByKey / window
是否跨 batch 只看当前 batch 跨 batch 累计状态
checkpoint 不需要 必须开

无状态转换每个 RDD 独立处理,干净利落。有状态转换要在 batch 之间记住状态,这个状态就是靠 checkpoint 落盘保住的。

维度二:无 shuffle vs 有 shuffle

map/flatMap/filter 是窄依赖,一个分区内独立算,不碰网络;join/groupByKey/reduceByKey 是宽依赖,要跨分区 shuffle,代价高一个量级。这和 RDD 的判断完全一致------DStream 的每个操作,最终都会落到对应 RDD 的某个操作上。


四、getOrCompute:DStream 怎么变成 RDD

这是 DStream 最核心的一个方法,把"按时间生成 RDD"这件事讲清楚。

JobGenerator 每个 batch 触发一次,传入时间点 t,然后对每个 DStream 调用 getOrCompute(t)

  1. 查缓存 :先看 generatedRDDs 里有没有 t 对应的 RDD,有就直接返回。
  2. 没命中就 compute(t) :调用当前 DStream 的 compute,它会先递归地让父 DStream 生成 t 时刻的 RDD,再应用当前这一步的转换。
  3. 缓存返回 :算出来的 RDD 存进 generatedRDDs,避免重复计算。

为什么要缓存?因为同一个 DStream 在同一个时间点可能被多个下游引用------比如一个流既要做实时统计,又要写一份原始数据。不缓存的话,同一个 RDD 会被算两遍,浪费整条链路的计算。


五、transform 和 foreachRDD:打通 RDD API

DStream 的算子就那么几十个,遇到复杂逻辑或者要访问外部资源(连接池、状态存储)时就不够用了。transformforeachRDD 就是为这个留的口子:

scala 复制代码
// transform:拿到 RDD,返回新的 RDD(继续流式计算)
val cleaned = ds.transform { rdd =>
  rdd.mapPartitions { iter => /* 每分区初始化一次连接 */ }
}

// foreachRDD:拿到 RDD,做输出(到此为止,不再返回)
ds.foreachRDD { rdd =>
  rdd.foreachPartition { partitionOfRecords =>
    /* 写外部存储 */
  }
}

两者都在 Driver 端直接拿到 RDD,可以随意用 RDD API。一个常见的最佳实践是:把连接这类重量级对象的创建放进 mapPartitions/foreachPartition,而不是在每条记录上 new 一个连接,否则吞吐会崩。


六、总结

  • DStream 是 RDD 的时间序列,不存数据,转换操作是惰性的。
  • DStreamGraph 分 Input/Transformed/ForEach 三类节点,Lineage 负责容错,有状态操作必须 checkpoint。
  • 转换按"无状态/有状态"和"无 shuffle/有 shuffle"两个维度判断,前者决定要不要 checkpoint,后者决定代价高低。
  • getOrCompute 是 DStream 到 RDD 的关键:查缓存 → 递归 compute → 缓存返回,缓存是为了不让同一时间点的 RDD 被重复计算。
  • 复杂逻辑用 transform/foreachRDD 打通 RDD API,连接等重对象放分区级初始化。

作者 :大数据技术实践者

博客blog.starzy.cn

GitHubstarzy1990.github.io

专注 AI Agent · LangGraph · RAG · 大数据架构 · 数据工程实践

相关推荐
2601_9632827711 小时前
政企无线对讲系统落地:从设备采购到长期运维的完整工程实践
大数据·运维·数据库
2601_9622184711 小时前
万象生鲜系统协议价底层架构实现生鲜企业报价业务数字化管理
大数据·运维·微服务·云原生·架构
南京兴帝文化传媒有限公司12 小时前
地图SEO与AI搜索优化结合实践:宁国摄影工作室本地获客案例分析
大数据·前端·人工智能·geo 优化·geo优化避坑
长谷深风11112 小时前
AI自动化中的关键闸门:何时必须人工审批?
大数据·人工智能·ai·自动化·ai agent·智能体·hitl
天辛大师13 小时前
天辛大师谈AI时代的沉思录,All in AI 持续做事与放大效应
大数据·人工智能·随机森林·重构·启发式算法
智码看视界13 小时前
大数据架构深度解析:StarRocks 实时数仓搭建:比 ClickHouse 更适合多维分析的场景
大数据·starrocks·olap·数据建模·列式存储·clickhouse对比
韦韦(Carina)13 小时前
技术博客结构化:让 AI 准确引用你的 7 条排版铁律
大数据·人工智能
IT毕设实战小研13 小时前
基于大数据处理的京东商品销售态势分析与可视化设计
大数据·科技·机器学习·信息可视化·数据分析
一比七品牌咨询13 小时前
科技企业品牌定位:如何把技术优势转化为品牌优势?
大数据·人工智能·品牌策划·品牌全案策划·深圳品牌策划公司·品牌定位
QQ_216962909613 小时前
基于微服务架构的店铺管理系统的设计与实现
大数据·spring boot·后端·spring·微服务·小程序·架构