cswork / 系统设计

第 34 章 / 共 57 章 · 约 4 分钟

关键技术:Flink 与流处理

本页目录
  1. 1. 核心模型
  2. 2. 什么时候用
  3. 3. 什么时候不适合
  4. 4. 关键限制和数量级
  5. 5. 面试中常被追问的点
  6. 6. 和其他技术的取舍
  7. 7. 本站案例
  8. 章节测验

流处理引擎(以 Flink 为代表)持续地读取事件流,在内存或本地磁盘里维护状态,边到边算,把结果写到下游。和“每条消息一个消费者处理”相比,它多了三样东西:按 key 分布的大状态、基于事件时间的窗口,以及故障后状态不丢、不重复计算的检查点机制。实时计数、Top-K、实时特征、风控规则,常常需要它。

1. 核心模型

  • 数据流图:source(通常是 Kafka)→ 一串算子(map、filter、keyBy、window、join)→ sink(数据库、缓存、另一个 topic)。
  • 按 key 分区的状态:keyBy(userId) 之后,同一个 key 的事件总由同一个并行实例处理,它的状态(计数、最近 10 次交易)就放在那个实例本地。状态可以放在堆内存,也可以用 RocksDB 放到本地磁盘,大到 TB 级。
  • 事件时间和水位线:按事件发生的时间而不是到达的时间计算。水位线(watermark)表示“时间早于 T 的事件应该都到了”,窗口在水位线越过窗口末尾时触发。允许迟到(allowed lateness)之后才到的事件,可以丢弃,也可以送到旁路输出单独处理。
  • 窗口:滚动窗口(每分钟一个)、滑动窗口(最近 5 分钟、每分钟更新一次)、会话窗口(不活跃超过 30 分钟就切开)。
  • 检查点(checkpoint):定期在数据流里插入屏障,各算子把状态快照存到持久存储,同时记下 source 的 offset。故障后从最近的检查点恢复状态,并把 source 倒回对应的 offset 重放,所以状态是“恰好一次”。要让外部结果也恰好一次,sink 必须是事务性的(两阶段提交写 Kafka)或者幂等的(按主键 upsert)。

2. 什么时候用

  • 实时聚合:广告每分钟点击数、视频实时播放量、按地区的订单量。
  • 实时 Top-K 和热点检测:最近一小时的热搜、异常流量的 IP。
  • 实时特征和风控:用户最近 10 分钟的交易次数、金额、设备变化,喂给模型或规则,见支付欺诈检测案例。
  • 流式 ETL 和物化视图:清洗、打宽、关联维表后写入数仓或搜索索引。
  • 复杂事件处理:“同一账户 5 分钟内在三个国家登录”这类模式匹配。

3. 什么时候不适合

  • 逐条独立处理:发通知、调接口、写一条记录,普通的 Kafka 消费者服务就够了,不需要一个流处理集群。
  • 离线批处理:每天跑一次的报表,用 Spark 或数仓 SQL 更简单。
  • 分钟级延迟就可以:微批(每几分钟跑一次小批任务)通常更容易维护。
  • 团队没有运维能力:状态、检查点、版本升级、反压都需要经验,托管服务能减轻不少负担。

4. 关键限制和数量级

维度要记住的事
延迟毫秒到秒级;窗口结果要等水位线,延迟至少是窗口长度加允许的乱序时间
吞吐随并行度水平扩展,集群每秒处理百万级事件很常见
状态RocksDB 状态后端可以到 TB 级;状态越大,检查点和恢复越慢
检查点间隔常见几十秒到几分钟;间隔越长,故障后要重放的数据越多
热点 key一个 key 的所有事件都落在同一个并行实例上,热门 key 会让这个实例成为瓶颈

5. 面试中常被追问的点

  • 迟到的事件怎么处理? 水位线留一点乱序容忍(例如 10 秒),再加允许迟到时间,窗口结果可以更新;超过的事件进旁路输出,事后修正,或者接受少量误差。
  • 故障恢复会不会重复计数? 内部状态靠检查点恢复,不会重复;写到外部时,用事务性 sink 或者按“窗口 + key”做主键 upsert,重放时覆盖而不是累加。
  • 热门 key 怎么办? 两阶段聚合:先按 key + 随机后缀 做局部聚合,再按 key 合并。
  • 代码改了,历史数据要重算? 从 Kafka 的旧 offset 重放(Kappa 架构);保留期不够时,从数据湖里的原始事件回填。Lambda 架构(批和流两套代码)现在用得少了,因为两套逻辑很难保持一致。
  • 为什么不在每个服务里自己维护计数? 多实例并发、故障恢复、乱序和窗口语义都要自己实现,很容易出错。

6. 和其他技术的取舍

选项适合代价
Flink低延迟、大状态、事件时间语义、恰好一次集群和状态的运维成本
Spark Structured Streaming已经在用 Spark、能接受微批延迟延迟通常是秒级以上
Kafka Streams输入输出都是 Kafka,作为库嵌入服务和 Kafka 绑定,分布式能力有限
普通消费者服务无状态或状态很小的逐条处理窗口、乱序、恢复都要自己写
批处理(Spark、数仓 SQL)每小时、每天的离线计算结果有延迟

7. 本站案例

章节测验

5 道选择题
  1. 统计“每分钟广告点击数”时,有的点击事件因为网络原因晚到了 20 秒。按什么时间算、怎么处理才准确?
  2. Flink 作业崩溃后从检查点恢复,结果写到外部数据库。怎样保证结果不会被重复累加?
  3. 按 video_id 聚合实时播放量,一个爆款视频让某个并行实例成了瓶颈。怎么办?
  4. 下面哪个需求不需要引入 Flink 这样的流处理引擎?
  5. 流处理逻辑有 bug,修复后需要重算过去 3 天的结果,Kafka 保留期是 7 天。最直接的做法是?

章节讨论

正在加载讨论…