凌晨两点,某电商平台的实时风控系统突然报警。工程师盯着屏幕上的延迟曲线——同样的流量,两套代码,一个稳如老狗,一个开始丢数据。这不是故障演练,是Spark Streaming和Structured Streaming在真实业务里的正面交锋。

老派方案:把流切成一块一块处理

打开网易新闻 查看精彩图片

Spark Streaming诞生于2014年,Spark 1.0版本。它的核心思路很直白:既然批处理成熟,那把实时流也切成小批不就行了?

这个模型叫DStream(离散化流)。系统每隔几秒(通常2-10秒)切一刀,把这段时间的数据包成一个RDD(弹性分布式数据集),然后用批处理那套算子——map、filter、reduceByKey——挨个处理。

2014到2018年间,这套方案撑起了大量早期实时业务。日志监控、点击流追踪、简单的实时大屏,DStream都能应付。团队不用学新东西,会写Spark批处理就会写流处理。

但切批的代价很快暴露。DStream操作的是裸RDD,没有Schema约束,没有SQL支持。更头疼的是事件时间处理——用户3:05产生的行为,3:12才到服务器,这种乱序数据需要工程师自己写补偿逻辑。「Apache Spark Services团队在处理DStream的事件时间问题时,经常需要构建自定义的变通方案」,原文这样描述当时的困境。

延迟数据、乱序数据、窗口聚合的准确性,这些在批处理里不存在的概念,在流场景下成了硬骨头。DStream把问题抛给了开发者。

新方案:把流当成一张永远增长的表

Spark 2.0带来结构性转变。Structured Streaming不再切批,而是把数据流看作「无界表」——新记录不断追加进来,查询在表上持续运行。

关键设计在这里:用DataFrame/Dataset API写查询,同一套代码,批和流无缝切换。SQL风格的聚合、Join、窗口函数,全部原生支持。开发者不需要在两种心智模型之间来回切换。

事件时间处理被内置了。系统引入watermark(水印)机制:你设定一个容忍阈值,比如10分钟,引擎会等待迟到数据直到超时,再输出最终的窗口结果。电商平台的订单流可以配置为:按订单时间戳等10分钟,迟到的记录照样计入统计。

这个设计把「流处理的复杂性」从业务代码下沉到了引擎层。工程师写声明式查询,引擎操心一致性、容错、状态管理。

同一个人,两种完全不同的工作方式

假设一个场景:实时统计各城市过去5分钟的订单总额。

用DStream的工程师要手动维护状态,处理乱序,考虑容错恢复。代码里充斥着updateStateByKey的样板逻辑,测试困难,调参靠猜。

用Structured Streaming的工程师写一段SQL:GROUP BY window(eventTime, "5 minutes"), city。水印自动兜底迟到数据,Checkpoint自动保障Exactly-Once。代码量可能只有前者的三分之一。

但这不是简单的「新老替代」。DStream在某些场景仍有生存空间——超低延迟(亚秒级)、与遗留RDD生态深度绑定的系统、或者团队确实没有Schema化改造的动力。

Structured Streaming的代价是更高的抽象层带来的黑盒感。当作业延迟飙升,你需要理解状态存储的RocksDB调优、输出模式的语义差异、以及Checkpoint与WAL的交互机制。

选型背后的组织信号

技术选型从来不只是技术问题。观察一个团队的选择,能读出很多信息。

坚持DStream的团队,往往有历史包袱:2016年前后搭建的平台,数百个作业,迁移成本被反复评估后搁置。或者业务场景极其简单,切批的延迟可接受,没有事件时间处理的刚需。

全面转向Structured Streaming的团队,通常经历了痛彻的教训——某次大促期间,DStream的手动状态管理在流量峰值下出错,导致财务统计偏差。或者团队刚从批处理转型,希望统一技术栈,降低认知负荷。

更微妙的是「混合态」:核心链路用Structured Streaming保障正确性,边缘监控用DStream快速迭代。这种分层本身说明,两种模型在工程实践中有明确的边界划分。

一个被忽略的长期成本

Spark 3.x版本对Structured Streaming的投入持续加大。新的流式Join优化、增量执行模式、与Delta Lake的深度集成,这些特性都不会回溯到DStream。

这意味着选择DStream不仅是选择一种编程模型,更是选择一条逐渐收窄的技术路线。社区维护的重心已经明确转移,未来遇到深层次的Bug或性能瓶颈,修复周期会显著拉长。

但「未来」是多远的未来?对于生命周期只剩18个月的业务系统,这个考量权重为零。对于计划服役五年的平台基础设施,这是决定性因素。

技术债务的残酷之处在于,它不会在你做选择时显形,只会在某个凌晨的报警电话里突然摊牌。

你的团队现在处于哪个阶段?是还在维护2017年写的DStream作业,还是已经在Structured Streaming上踩过生产环境的坑?如果今天从零开始设计一套实时风控系统,你会给团队设定怎样的技术约束?