Spark 4.3 发布:有状态实时流 P99 延迟低于 150 毫秒,小数据迭代也更快
原文信息:本文译自《Introducing Apache Spark® 4.3 - Now available in Databricks Runtime 19》,作者 Jungtaek Lim、Wenchen Fan、Andreas Neumann、Serge Rielau、Hyukjin Kwon、Jerry Peng、DB Tsai、Daniel Tenedorio、Xiao Li、Reynold Xin,发布于 2026 年 11 月 5 日。原文链接:https://www.databricks.com/blog/introducing-apache-spark-43-now-available-databricks-runtime-19 。原文未注明许可信息。本文为翻译,译者在文末补充了译者注。
译者导语:流处理最难的从来不是算得快,而是既要快又要记得住——反欺诈要看一笔交易之前的行为,用户画像要跟着最近的动作实时变,设备监控要一边计数一边汇总。Spark 4.3 这次把有状态处理带进了低延迟的实时模式,三类典型负载的 P99 延迟都压到了 150 毫秒以内。下面是官方公告的翻译。
流处理终于记住了上下文
反欺诈信号不能只看最新一笔交易,还得看它之前发生过什么;个性化推荐要反映用户最近的行为;设备监控要随着新读数不断更新计数和汇总。这类应用既要低延迟,又要带状态。
Spark 4.3 把实时模式(Real-Time Mode)扩展到了这些有状态模式,成为一个完整的流处理引擎:支持流式聚合、dropDuplicates 去重,以及 Scala/Java 的 transformWithState 自定义有状态处理。开发者继续用结构化流(Structured Streaming)熟悉的编程模型维护带键的状态、更新结果,不必为了更低的延迟再引入另一套引擎。
三类代表性有状态负载的实测结果(SPARK-58884)如下,P99 端到端延迟全部低于 150 毫秒:
| 有状态负载 | P99 端到端延迟 |
|---|---|
| dropDuplicates 去重 | 136 毫秒 |
| 窗口聚合 | 139 毫秒 |
| transformWithState 自定义状态处理 | 141 毫秒 |
需要注意:这是三类受测负载的实测数字,不是所有管线都能保证的延迟。
实时模式建立在结构化流之上,没有另起一套编程模型。有状态算子继续用 Spark 已有的状态存储(state store)和检查点(checkpointing)机制来保证持久化和故障恢复。
执行引擎改成了“边产边消费”
要支撑有状态实时模式,Spark 必须改掉舞台(stage)调度和连接方式。传统的阻塞式 Shuffle 要求生产阶段跑完,下游阶段才能开始,这种模式撑不起长跑流任务之间的连续数据流。
Spark 4.3 引入了原生的并发阶段调度和流水线 Shuffle(pipelined shuffle),生产阶段和消费阶段可以同时运行。流式 Shuffle(Streaming Shuffle)把这套能力带给实时模式:记录按需增量地传给负责对应键的下游任务,不用等整个生产阶段跑完。执行方式从“生产阶段 → 物化 Shuffle → 消费阶段”变成了“生产阶段 ⇄ 流式 Shuffle ⇄ 消费阶段”。
为此 Spark 新增了 PipelinedShuffleDependency 这种 Shuffle 依赖:消费者可以在生产者还在跑的时候就开始读。调度器把这类依赖连接的阶段看作一个流水线组(pipelined group),统一协调调度:组内阶段一起调度,一个阶段失败,整个组一起失败——同生共死。因为组内所有阶段并发运行,只有任务槽位够用时 Spark 才会接纳这个组,否则快速失败,而不是挂起等待。
Shuffle 的路由规则也按依赖类型区分:普通 ShuffleDependency 走阻塞式 Shuffle 管理器,先把全部 Shuffle 输出物化下来,下游再读,内置实现是 SortShuffleManager;PipelinedShuffleDependency 走流水线 Shuffle 管理器,输出一边产生一边可用,内置实现是 StreamingShuffleManager。依赖类型定的是执行契约,Shuffle 管理器提供数据传输机制,调度器协调流水线阶段时不用关心是哪个负载创建了它们。
这套底座的意义不止于实时模式。并发阶段调度、流水线数据交换和协调执行是通用的引擎能力,4.3 先用它实现了低延迟有状态处理,往后还能支撑其他需要跳出“一次一个阶段”执行模型的负载。
Project Feather:小数据迭代别再等调度
大模型辅助开发常常是短反馈循环:生成一段转换逻辑、在样本上跑一下、看结果、再改。每轮可能只处理一个小文件或几行数据,可任务调度和 Shuffle 的开销比计算本身还大。
Project Feather(羽毛计划)瞄准的就是这部分固定的执行开销,让 Spark 在小数据集上探索和验证 SQL 与 DataFrame 代码时更灵敏。开发者从样本做到大规模负载用的是同一套 API,不管代码是自己写的还是 AI 助手给的。
Spark 4.3 引入了可选的无 Shuffle 单任务执行:符合条件的小型单分区查询片段可以跳过 Shuffle。满足条件时,扫描算子报告输出已经在一个分区里,下游支持的操作就不用再走一次不必要的 Shuffle。默认的适用范围是:符合条件的文件扫描读取一个非空小文件、刚好装进一个分区;本地内存关系最多 1000 行。支持的查询形态包括部分扫描、聚合、去重、窗口、排序和 Limit。
Feather 还增加了可选的 Arrow 原生数据集缓存,把缓存的批次以 Arrow 格式存储,带压缩和列统计信息。Arrow 格式的输入可以少做一次转换,适合反复探索同一份数据的场景。单任务优化和 Arrow 缓存都是可选开启:启动全新 Spark 应用时设置静态配置 spark.sql.cache.serializer=org.apache.spark.sql.execution.columnar.ArrowCachedBatchSerializer 即可。更广的进程内本地重分区是下一版的跟进工作。
SCD Type 2 自动 CDC:记录的历史也要留下来
一张只存当前状态的表能告诉你客户今天住在哪里,却说不清下单那天他属于哪个区域。报表、审计和历史分析往往两者都要。Spark 4.3 给自动 CDC(Auto CDC,变更数据捕获)补上了端到端的缓慢变化维(SCD,Slowly Changing Dimension)Type 2 支持,管线可以跟踪每条记录的完整变化历史,而不是每次变更都覆盖最新值。这项能力通过 Python、SQL 和 Spark Connect 都能用,变更自动应用,少写手工合并逻辑。
举个例子:跟踪客户状态和区域随时间的变化。SCD Type 2 的输出表里,每个唯一键对应一串记录,每条记录用 __START_AT 和 __END_AT 两个元数据列标出有效期的起止。比如一条变更记录说 id 为 17 的客户从 West 区域搬到了 East,管线会保留区域为 West 的旧记录,把它的结束时间标为这条变更记录的事件时间;同时插入一条区域为 East 的新记录,开始时间就是该变更的事件时间。分析时就能按事件时间取到当时适用的区域。
底层实现上,自动 CDC 把声明式流定义和目标表结构推导、专用的 SCD Type 2 流处理连在一起:键标识实体,排序决定变更顺序,历史跟踪列指定哪些属性变化要产生历史版本。引擎还会校验保留列、排序类型和历史跟踪配置,并在目标表结构演进后协调较窄的微批次。
有一点要注意:自动 CDC 每个微批次用 MERGE 写目标表,所以目标表必须放在实现了 Data Source V2 行级操作的目录连接器里,Spark 内置的文件格式(包括 Parquet)不满足条件,常见选择是 Iceberg。
Spark Connect:开发、观测、运维都更顺手
应用和 AI 智能体需要从自己的进程里调用 Spark、管理会话,并把一次失败的请求对应到服务器上实际跑的工作。Spark 4.3 从会话与操作可见性、请求归属和更广的客户端支持三方面强化了 Spark Connect。
Spark Connect 通过 gRPC 把 DataFrame 操作以逻辑计划的形式发给服务器执行,再以 Arrow 批次把结果流回来。4.3 新增了 REST 端点,可以列出和检查 Connect 会话与操作,还给操作加了 ID,方便跨客户端和服务器追踪执行。在 POSIX 系统上做本地 PySpark 开发时,实验性的 SPARK_LOCAL_CONNECT_REUSE=1 选项可以在多次运行之间复用一个持久的 Connect 服务器,省掉反复启动的开销。PySpark 的 SQLContext 兼容包装器现在也能配合 Spark Connect 使用。
Spark Connect 的 JDBC 驱动新增了 ARRAY、MAP、STRUCT 结果支持和更丰富的 ResultSet 处理、SQL 类型元数据和更兼容的模式内省,以及更好的异常处理。API 覆盖面也扩大了:DataFrame.zip、SCD Type 2 自动 CDC、纳秒时间戳类型和字面量。另外,社区还在推进 Spark Connect Gateway(会话感知路由与多租户运行)、Rust 和基于 Rust 的 Python 客户端等工作,不过这些有各自的开发时间线,不属于 4.3 的功能。
PySpark:API 更丰富,执行更省事
Python 流程常常要分几步生成派生列、再把结果拼起来,还要在 pandas、Arrow 和 Spark 之间来回搬数据。Spark 4.3 让更多这类工作可以直接用 DataFrame API 完成,同时优化了底层的执行路径。
DataFrame.zip 提供了一种方式,可以把同一输入的两个兼容列投影拼在一起,不用下沉到 RDD API(Spark Connect 下也支持)。比如把标识列和一个派生值拼起来:id=1 配 doubled=20,id=2 配 doubled=40。zip 只合并同一底层计划的纯投影变换,两边的过滤、排序、连接或聚合必须完全一致,才能保证行对齐。
更广的执行优化针对数据传输、批处理和原生计算:基于 Pickle 的 UDF 输入批次可以按字节数限定,而不只是按行数,行大小不均匀时控制更好;批量的 pandas-on-Spark 投影和更多原生 NumPy 实现减少了不必要的工作;pandas 到 Spark 的转换新增了对 PyArrow ChunkedArray 列的支持。
另外还增加了已有向量函数的 Python 与 Scala 绑定、分组协方差与描述统计、滚动与扩展中位数和标准误操作,以及 show_versions 诊断。Traceback 本地变量默认不开启,因为其中可能包含敏感数据。
Spark SQL:给分析与 AI 准备更好的上下文
事件载荷常以嵌套文档的形式到来,靠得很近的两个事件可能需要比微秒更高的精度。为这些输入准备数据,本不该每类转换都另写一套自定义处理层。Spark 4.3 扩展了面向半结构化数据与时序分析的原生 SQL,包括文档操作、时序匹配和可选的纳秒时间戳预览。
纳秒时间戳还是预览功能(可能有破坏性变更)。启用后,两个只差一纳秒的时间戳能被正确区分,最后一位数字会被保留而不是丢弃。预览支持 TIMESTAMP_NTZ(p) 和 TIMESTAMP_LTZ(p),精度 p 从 7 到 9。纳秒支持贯穿解析、内部值表示、类型转换、表达式、Spark Connect、缓存和数据源集成,新增的 timestamp_nanos 和 unix_nanos 函数处理数值转换。存储与连接器的支持因数据路径而异,落地前需要端到端验证。
嵌套 JSON 的处理也更顺手:JSON_TABLE 把数组展开和类型化字段提取合在一起,VARIANT 变换让管线可以直接更新或删除字段,不用再写自定义文档处理 UDF。两者合起来,简化了为分析、数据丰富化(enrichment)和检索管线准备内容的过程。JSON_TABLE 对每行的各列共用一次遍历解析,避免每列单独解析一次;VARIANT 字段提取下推也扩展到了支持的聚合、排序和连接查询。后续的 SQL/JSON 工作还会带来 JSON_VALUE、JSON_EXISTS、JSON_QUERY,但那是跟进方向,不是 4.3 的功能。
时序匹配方面,Spark 4.3 的 SQL ASOF JOIN(预览功能,可能有破坏性变更)直接表达了“按当时可用的信息匹配”:历史分析与模型评估需要的是事件发生时就已知的上下文,而不是事后补上的信息。以前要手写范围连接再排序,现在一条 ASOF JOIN 就能表达。比如用 available_at 记录某个特征版本何时可用:10:03 的一次预测会匹配到 10:00 可用的特征版本,而不是 10:05 的。专用的排序合并执行路径让这种匹配成为引擎操作,而不是应用层的查询逻辑。训练数据准备、评估和事故分析里的按时间点 enrichment(特征丰富)都因此更简单,前提是可用时间戳确实反映了当时已知的信息。
其他改进与致谢
日常数据处理也有不少新 API 与性能改进:SQL 新增 ANSI UNNEST(把数组展开成行)、counter_diff 窗口函数(把累计计数转成增量)和 jaro_winkler_similarity 字符串相似度函数;支持的文件源新增了读取 tar、zip、7z 压缩包的可选支持,省掉单独解压的步骤;引擎改进了聚合执行,符合条件的 UNION 操作默认启用全阶段代码生成;可选优化把外连接与反连接里的空连接键分散到多个 Shuffle 分区;嵌套数据处理在结构体数组里剪掉更多无用字段,更多数组高阶函数支持代码生成;向量化 Parquet 读取改进了类型转换与字典解码。
Spark 4.3 凝聚了 Apache Spark 社区的力量,200 多位贡献者处理了 1100 多个 Jira 工单,官方在文末感谢了所有参与代码、评审、测试、文档和反馈的人。
(原文配图:Spark 4.3 发布头图,见原文链接)
想上手 Spark 4.3,可以去 Apache Spark 下载页获取发布包和文档,升级前先看迁移指南;Databricks 上的可用情况见 Databricks Runtime 发布说明。
译者注
- 150 毫秒是受测负载的数字,不是承诺:136、139、141 毫秒这三个 P99 数字来自官方报告的三类代表性负载,原文明确说了不是对所有管线的保证。自己的流作业能不能进这个区间,取决于状态大小、键分布和检查点开销,升级前先在影子流量上跑一轮再定 SLA。
- 先分清哪些是正式功能、哪些是预览:纳秒时间戳和 ASOF JOIN 都是预览功能,原文写明“可能有破坏性变更”;Project Feather 的单任务执行和 Arrow 缓存、以及纳秒支持都要显式开启。生产管线用预览功能之前,先确认升级路径和回滚方案。
- 和华为云的结合点:流批一体的思路是通用的——一套引擎既跑批处理又跑毫秒级流,省掉为低延迟单独维护一套流引擎的成本。华为云 MRS(MapReduce 服务)与 DLI(数据湖探索)也是这个打法:Spark 引擎跑批与流、湖仓一体,实时聚合与历史回填可以在同一套数据湖架构里做,选型时可以按这个思路对照。
相关阅读(同方向旧文,纯文本链接): - SQL 共现统计慢在哪?Tessera 用稀疏乘法加 AMX 做连接聚合,比 DuckDB 快最高 1802 倍:https://bbs.huaweicloud.com/blogs/d76945b1150e48c184016490b1be3d33 - 数据湖分析智能体自己造工具:TEAL 无标注演化目录与索引,紧预算下准确率提升:https://bbs.huaweicloud.com/blogs/b5fa0cbdf56b4ec981b88888f57f9930
原文:Introducing Apache Spark® 4.3 - Now available in Databricks Runtime 19,Jungtaek Lim、Wenchen Fan、Andreas Neumann、Serge Rielau、Hyukjin Kwon、Jerry Peng、DB Tsai、Daniel Tenedorio、Xiao Li、Reynold Xin,Databricks Blog,2026-11-05。原文链接:https://www.databricks.com/blog/introducing-apache-spark-43-now-available-databricks-runtime-19
- 点赞
- 收藏
- 关注作者
评论(0)