4、实时计算引擎:Flink/Kafka Streams基础、事件时间与处理时间、状态后端选择、Checkpoint机制
好,咱们进入第四章。这一章聊的是实时计算引擎,说白了就是用户画像系统的心脏。你前面把数据接进来了,标签规则也定好了,但谁来算?怎么算?算到一半挂了怎么办?这些都得靠实时计算引擎来兜底。
我个人习惯把实时计算引擎比作「流水线上的工人」。Flink 和 Kafka Streams 就是两个最靠谱的工人。它们各有脾气,你得摸透了才能用好。
4.1 Flink 与 Kafka Streams:选谁更合适?
先说说 Flink。我最早接触 Flink 是 2018 年,那时候它刚火起来。说实话,第一眼就被它的「精确一次」语义吸引了。你想想看,做用户画像,用户的积分、等级、标签,差一个数都不行。Flink 在这块确实稳。
Flink 的核心优势在于:
- 真正的流批一体:同一套 API 既能跑流也能跑批。我在项目中遇到过,白天跑实时标签,晚上用同一套代码跑历史数据回溯,省了不少事。
- 强大的状态管理:用户画像需要维护大量用户状态,比如「过去7天浏览次数」、「最近一次登录时间」。Flink 的状态后端把这些管得明明白白。
- 生态丰富:跟 Hive、HBase、Elasticsearch 都能无缝对接。
再聊聊 Kafka Streams。它轻量,真的轻。不需要单独部署集群,嵌在你的应用里就能跑。我有个小项目,用户量不大,就几十万,用 Kafka Streams 搭了个实时标签计算,两周就上线了。
但要注意,Kafka Streams 的短板也很明显:
- 状态管理相对简陋,复杂窗口操作不如 Flink 顺手
- 没有 Flink 那样的 Checkpoint 机制,故障恢复得靠 Kafka 自身的 offset 管理
4.2 事件时间与处理时间:别搞混了
嗯,这里要注意。很多新手在这块栽过跟头。我刚开始做实时画像时,就吃过一次亏。
什么叫事件时间?就是用户真正发生行为的时间。比如用户凌晨1点在 App 上点了个赞,这个时间就是事件时间。什么叫处理时间?就是你的 Flink 任务收到这条数据的时间。网络延迟、系统拥堵,处理时间可能比事件时间晚好几个小时。
为什么会这样?你想想看,用户手机信号不好,数据在本地缓存了3小时才发出来。这时候你用处理时间算「今日活跃用户」,那这个用户就被漏掉了。画像就不准了。
Flink 里怎么配?看这段代码:
// 设置事件时间语义
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// 定义 Watermark 策略
DataStream<UserEvent> stream = env
.addSource(kafkaConsumer)
.assignTimestampsAndWatermarks(
WatermarkStrategy.<UserEvent>forBoundedOutOfOrderness(Duration.ofSeconds(10))
.withTimestampAssigner((event, timestamp) -> event.getEventTime())
);
这里有个坑。Watermark 的延迟时间设多少?设短了,乱序数据丢得多;设长了,实时性变差。我一般先设 10 秒,上线后观察数据延迟情况再调。记住,没有完美的值,只有适合你业务的值。
4.3 状态后端选择:内存、文件系统还是 RocksDB?
状态后端,说白了就是你的用户状态存哪儿。Flink 给了三个选项:
| 状态后端 | 存储位置 | 适用场景 | 我的经验 |
|---|---|---|---|
| MemoryStateBackend | TaskManager 内存 | 小状态、调试用 | 千万别上生产,一挂全丢 |
| FsStateBackend | TaskManager 内存 + 文件系统 | 中等状态、高吞吐 | 适合状态量在 GB 级别 |
| RocksDBStateBackend | 本地磁盘 + 文件系统 | 超大状态、TB 级别 | 用户画像首选,稳 |
我个人习惯,用户画像系统一律用 RocksDBStateBackend。为什么?因为用户画像的状态太大了。每个用户存几十个标签,百万用户就是几千万个键值对。内存根本扛不住。
配置 RocksDB 也很简单:
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new RocksDBStateBackend("hdfs://namenode:8020/flink/checkpoints", true));
注意第二个参数,true 表示增量 Checkpoint。这个一定要开,不然每次全量备份,大状态能把你 HDFS 写爆。
4.4 Checkpoint 机制:你的救命稻草
做实时计算,最怕什么?任务挂了。挂了不可怕,可怕的是挂了之后状态丢了,用户标签全乱套。
Flink 的 Checkpoint 机制就是干这个的。它定期把状态快照保存下来。任务重启时,从最近的 Checkpoint 恢复,数据一条不丢,状态一个不错。
Checkpoint 的核心参数就几个:
- 间隔时间:默认 10 秒。我一般设 30 秒到 1 分钟。太频繁影响性能,太稀疏恢复时丢数据多。
- 最小间隔:两次 Checkpoint 之间至少隔多久。防止 Checkpoint 风暴。
- 超时时间:一次 Checkpoint 超过这个时间就算失败。默认 10 分钟,大状态可以调大。
配置示例:
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(30000); // 30 秒一次
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(10000); // 最小间隔 10 秒
env.getCheckpointConfig().setCheckpointTimeout(600000); // 超时 10 分钟
env.getCheckpointConfig().setTolerableCheckpointFailureNumber(3); // 容忍 3 次失败
最后说一句,Checkpoint 不是万能的。它保证的是「精确一次」语义,但代价是性能损耗。如果你的业务允许丢少量数据(比如实时大屏展示),可以调大间隔甚至关闭 Checkpoint。但做用户画像,我建议你老老实实开着。
好了,这一章就到这儿。下一章咱们聊聊标签计算的具体实现,包括怎么用 Flink SQL 写标签规则、怎么处理迟到的数据。到时候见。