Structured Streaming 的工作方式
Structured Streaming 把流数据当成一张不断追加的表,用与批处理相同的 API 写逻辑,引擎按微批执行。输出模式决定结果如何写入下游:是只追加新增行,还是持续更新已有行。迟到数据靠 watermark 划定等待边界,超出的部分按策略丢弃或交给离线链路兜底。
基本信息
| 执行方式 | 微批为主,也有连续处理模式 |
|---|---|
| 输出模式 | append / update / complete |
| 迟到处理 | watermark 划定等待水位 |
| 落点 | 文件、表、或 forEachBatch 自定义 |
| 检查点 | checkpointLocation,必需用于故障恢复 |
典型使用场景
- 准实时落仓:Kafka 事件流按分钟微批落到湖表分区
- 实时聚合:按维度做分钟级汇总,结果表持续更新
- 流批一体:同一套逻辑先用流跑当天,再用批回刷历史
- 多落点写入:forEachBatch 里同时写明细与聚合结果
- 与离线对齐:流任务产出当天明细,离线任务次日重跑同一分区口径
微批写入示例
微批写入示例
q = (spark.readStream.format('kafka')
.option('kafka.bootstrap.servers', 'broker:9092')
.option('subscribe', 'trade_events')
.load()
.writeStream.format('parquet')
.option('checkpointLocation', '/ck/stream_trade') # 必需:断点续跑
.option('path', '/warehouse/stream/trade')
.trigger(processingTime='1 minute')
.outputMode('append')
.start())输出模式对照
| 模式 | 写入行为 | 适用查询 |
|---|---|---|
| append | 只写新增行 | 无聚合或带 watermark 的聚合 |
| update | 只写有变化的行 | 带聚合需持续更新结果 |
| complete | 重写整张结果表 | 结果集很小的聚合统计 |
| forEachBatch | 自定义每次微批的写出 | 需写多个落点或做幂等控制 |
watermark 的等待时长要与业务容忍度对齐:设得太短会丢掉可用的迟到数据,设得太长则状态持续占用内存,检查点体积随之增大。
没有 checkpointLocation 的流任务无法恢复进度,重启后会从最早的偏移重新消费,产出重复数据且难以事后去重。
流与离线的口径怎么对齐
常见做法是让流任务只负责及时性,离线任务在次日重跑同一天分区,用离线结果覆盖流结果。这样既能满足小时级的看数需求,又能保证最终口径与批处理一致。前提是两套逻辑使用同一份 SQL 或同一个视图定义,避免两边各写一份。
上线前检查项
- 确认检查点目录在共享存储上,且不会被清理任务误删
- 确认输出模式与查询类型匹配,聚合查询不要直接配 append
- 明确迟到数据的处置路径:丢弃、修正批次还是交给离线回刷
- 压测单批处理时长,确保小于触发间隔以避免持续堆积
- 记录消费起始偏移,方便事故后按偏移重放