实时计算基础:流式数据处理
好,咱们正式开始第二章节。上一章聊了实时指标的整体架构,这一章得把地基打牢——流式数据处理到底是什么?它跟批处理有啥区别?主流的框架怎么选?
我记得刚接触实时计算那会儿,脑子里全是“流”和“批”的浆糊。后来踩了不少坑,才慢慢理清楚。今天我把这些经验掰开揉碎,给你讲明白。
数据流 vs 批处理:本质差异
先看一个最直观的例子。
假设你要统计过去24小时网站的PV(页面浏览量)。
- 批处理:等到24点整,把今天所有的日志文件一次性读进来,跑个MapReduce或Spark任务,算出总数。数据是“静止”的,你处理的是有限数据集。
- 流处理:每来一条日志,就立刻更新一次PV计数器。数据是“流动”的,你处理的是无限数据集。
说白了,批处理是“先存后算”,流处理是“边来边算”。
核心区别一句话:批处理处理的是“过去的数据”,流处理处理的是“现在的数据”。
我在项目中遇到过一种尴尬情况:业务方要实时大屏,结果我们用了批处理,每5分钟跑一次。那5分钟窗口内的数据就是盲区。后来换成流处理,延迟降到秒级,大屏才真正“实时”起来。
流式数据处理的核心概念
要玩转流处理,这几个概念必须吃透。我按自己的理解给你捋一遍。
1. 事件时间 vs 处理时间
这是新手最容易搞混的。
- 事件时间(Event Time):数据实际发生的时间。比如用户点击按钮的那一刻,埋点日志里记录的时间戳。
- 处理时间(Processing Time):数据到达计算引擎的时间。比如Flink收到这条日志的服务器时间。
为什么重要?因为网络延迟、系统故障、数据乱序,都会导致事件时间和处理时间不一致。
我曾经踩过的坑:做交易风控时,直接用处理时间做窗口。结果某次网络抖动,一批延迟了10秒的交易被归到了下一个窗口,导致风控规则误判。后来全部改用事件时间 + Watermark,才解决问题。
2. Watermark(水位线)
Watermark是Flink里用来处理乱序数据的机制。它本质上是一个“时间进度标记”,告诉引擎:“到这个时间点为止,该来的数据基本都来了,没来的就当迟到处理。”
举个例子:
- 你设置Watermark延迟为5秒。
- 当前处理到事件时间12:00:00的数据。
- Watermark推进到11:59:55。
- 如果一条事件时间为11:59:50的数据现在才到,它还能被处理。
- 如果一条事件时间为11:59:40的数据现在才到,它就被标记为“迟到数据”。
嗯,这里要注意:Watermark不是万能的。延迟设置太短,丢数据;设置太长,窗口迟迟不触发。需要根据业务容忍度来调。
3. 窗口(Window)
流数据是无限的,但计算往往需要“切一刀”。窗口就是这把刀。
| 窗口类型 | 说明 | 典型场景 |
|---|---|---|
| 滚动窗口(Tumbling) | 固定大小,不重叠。比如每5分钟一个窗口。 | 每5分钟统计一次QPS |
| 滑动窗口(Sliding) | 固定大小,可重叠。比如窗口大小10分钟,每5分钟滑动一次。 | 最近10分钟的实时趋势 |
| 会话窗口(Session) | 根据不活动间隔切分。比如用户连续操作,超过30秒没动作就切一个窗口。 | 用户行为分析、会话时长统计 |
个人习惯:能用滚动窗口就别用滑动窗口。滑动窗口数据重叠,计算量大,而且容易产生重复统计。除非业务明确需要“最近N分钟”这种语义。
实时计算框架概览
目前主流的实时计算框架就两个:Apache Flink 和 Spark Streaming。我两个都用过,给你说说真实感受。
Flink:真正的流处理
Flink的设计哲学是“一切皆流”。它原生支持事件时间、精确一次语义(Exactly-Once)、状态管理。说白了,它就是为流处理而生的。
- 优势:低延迟(毫秒级)、状态一致性高、窗口机制灵活。
- 劣势:学习曲线陡峭、生态不如Spark成熟。
- 适合场景:金融风控、实时大屏、IoT数据管道。
Spark Streaming:微批处理
Spark Streaming本质上是把流数据切成一个个小批次(Micro-Batch),然后用Spark引擎去跑。它不是真正的流处理,而是“伪流”。
- 优势:生态丰富(SQL、MLlib)、与批处理统一、社区活跃。
- 劣势:延迟较高(秒级)、状态管理不如Flink精细。
- 适合场景:ETL清洗、准实时报表、与Spark批任务混用。
我的建议:如果延迟要求小于1秒,或者需要复杂事件时间语义,无脑选Flink。如果延迟容忍在秒级,且团队Spark经验丰富,Spark Streaming更稳妥。
框架对比一览
| 维度 | Flink | Spark Streaming |
|---|---|---|
| 处理模型 | 真正的流处理 | 微批处理 |
| 延迟 | 毫秒级 | 秒级 |
| 事件时间支持 | 原生支持 | 需额外配置 |
| 精确一次语义 | 原生支持 | 需配置 |
| 状态管理 | 强大(RocksDB、内存) | 较弱(基于RDD) |
| 学习成本 | 高 | 中 |
| 生态 | 中等 | 丰富 |
流处理核心架构图
下面这张图,是我自己总结的流处理核心逻辑。你仔细看一遍,基本就能理解数据是怎么从源头流到终点的。
避坑指南:我踩过的三个坑
最后,分享几个真实教训。你以后大概率也会遇到。
- 乱序数据导致结果不准:一开始没配Watermark,结果窗口关闭后还有数据进来,直接丢弃。后来加了Watermark + allowedLateness,才搞定。
- 状态膨胀导致OOM:Flink的状态默认存在内存里。某次业务量暴增,状态直接撑爆了JVM。后来换成RocksDB状态后端,才稳住。
- 背压(Backpressure):数据源写入速度远大于处理速度,导致任务积压。我当时的解决方案是:增加并行度 + 优化算子链 + 调整缓冲区大小。
一个小技巧:上线前一定要做压测。用模拟数据把流量打到峰值的2倍,看看任务会不会崩。别问我怎么知道的……