从入门到进阶:彻底吃透 Flink 中的 Watermark 和事件时间处理!(阿里云大数据面试)

第一章:流处理的时间维度,究竟是怎么一回事?

在批处理的世界里,时间好像并不重要。数据是一次性全部读取的,处理的顺序是确定的。但一进入流处理的领域,时间就变得特别关键,甚至可以说,流处理=数据+时间。而在Apache Flink这个强大的流处理框架中,时间的概念更是整个运行时引擎的基石。

我们通常在流处理场景中,会涉及三种时间:

  • 事件时间(Event Time):数据在实际发生时的时间戳(通常由设备或业务系统打上)。
  • 摄取时间(Ingestion Time):数据进入Flink系统的时间。
  • 处理时间(Processing Time):Flink所在机器当前的系统时间。

为什么要区分这三种时间?

举个简单的例子,如果你在做用户行为分析,一个用户在2025-06-01 08:00:00点击了“购买”按钮,但由于网络延迟,这条事件直到08:00:05才进入Flink系统,那么事件时间就是08:00:00,摄取时间是08:00:05,处理时间可能是08:00:06。

显然,如果你要做某种10分钟滚动窗口的计算,你是希望按照用户真实点击时间来归类的,这就是为什么事件时间在复杂流处理逻辑中才是真正王道。

不过——说到这里,不得不提个“坏消息”:现实中的数据从不听话。

第二章:乱序事件的挑战 —— 理想很丰满,现实很骨感

事件时间听起来很美好,但真实世界的数据流,可不会乖乖地按顺序排队来。

比如同一个设备采集的事件:

  • event1:timestamp = 08:00:01
  • event2:timestamp = 08:00:04
  • event3:timestamp = 08:00:03

你没看错,event3 是晚到的,典型的乱序数据。

如果你用事件时间来做计算,比如每5秒钟一个窗口,你就会面临一个严重的问题:数据到底什么时候算“到齐”了?

第三章:Watermark初探 —— 让时间流动起来

Watermark 是 Flink 中用于处理事件时间的核心机制。

什么是 Watermark?

一句话解释:Watermark 是系统对事件时间进度的推测。

换种说法,它告诉Flink:“我认为,时间戳 <= X 的数据差不多都到齐了,你可以安全地触发相应计算了。”

这意味着:Watermark 并不是时间戳,而是一种系统主观判断的机制。

Watermark 是如何产生和传播的?

通常来说,Watermark 是由 Source(数据源) 生成的,并且沿着 DataStream 向下游传播。

Flink 中典型的方式是:

DataStream<Event> stream = ...;
stream.assignTimestampsAndWatermarks(
    WatermarkStrategy
        .<Event>forBoundedOutOfOrderness(Duration.ofSeconds(5))
        .withTimestampAssigner((event, timestamp) -> event.getTimestamp())
);

上面这段代码意思是:我知道我的事件可能最多晚来5秒钟,所以我可以容忍5秒的乱序。

比如,当前观察到的最大事件时间是 08:00:10,那么 Watermark 就是 08:00:10 - 00:00:05 = 08:00:05

当 Watermark 推进到某个点时,就意味着窗口或某些基于时间的操作可以触发计算了。

为什么 Watermark 是“保守”的?

因为你永远不能确定未来有没有更早的事件还在路上,所以 Flink 设计得很保守。它宁可等一等,也不愿意错过数据。

这种保守也意味着:

  • 处理延迟可能上升。
  • 窗口计算触发会变慢。
  • 但是!准确性更高。

第四章:窗口触发机制中的 Watermark 角色

在 Flink 中,最常见的时间计算操作就是窗口,比如 tumbling(滚动)窗口、sliding(滑动)窗口、session(会话)窗口。

比如说一个 10 秒滚动窗口,按事件时间划分:

窗口1:08:00:00 - 08:00:10
窗口2:08:00:10 - 08:00:20
...

Flink 会等到 Watermark >= 窗口结束时间,才触发窗口计算。

所以如果 Watermark 是 08:00:09,那么第一个窗口不会触发。

而一旦 Watermark 达到 08:00:10,就意味着 Flink 认为“08:00:00 - 08:00:10”之间的事件差不多到齐了,于是窗口触发。

这一特性对于精确计算延迟事件非常关键。

第五章:自定义 Watermark 策略——与乱序和解的艺术

很多时候,预定义的乱序容忍策略并不能完全适配业务实际。

比如有些数据源的延迟特别严重,有些则基本准时。

这时候,你就可以实现自己的 WatermarkGenerator

自定义 WatermarkGenerator 示例

WatermarkStrategy<Event> strategy = WatermarkStrategy
    .<Event>forGenerator(ctx -> new WatermarkGenerator<Event>() {

        private long maxTimestampSeen = Long.MIN_VALUE;
        private final long maxOutOfOrderness = 5000; // 5秒

        @Override
        public void onEvent(Event event, long eventTimestamp, WatermarkOutput output) {
            maxTimestampSeen = Math.max(maxTimestampSeen, eventTimestamp);
        }

        @Override
        public void onPeriodicEmit(WatermarkOutput output) {
            output.emitWatermark(new Watermark(maxTimestampSeen - maxOutOfOrderness));
        }
    })
    .withTimestampAssigner((event, timestamp) -> event.getTimestamp());

上面的代码展示了典型的“观察最大时间戳+固定延迟”的策略。

你甚至可以根据业务规则,比如某种字段值、某类事件类型、甚至外部指标,动态调整 maxOutOfOrderness

延迟太大怎么办?

  • 可以设置 允许延迟事件的时间范围,例如使用 allowedLateness():
.window(TumblingEventTimeWindows.of(Time.seconds(10)))
.allowedLateness(Time.seconds(30))
  • 配合 侧输出流 SideOutput 接收迟到事件:
.windowAll(...)
.sideOutputLateData(lateTag)

这些机制可以最大程度减少“迟到就丢”的损失,同时也对系统资源消耗提出了挑战。

第六章:并行度与Watermark的“协奏曲”

现实世界中的数据流可不只有一个 Source。为了扩展处理能力,Flink 天然支持并行计算,即一个 Operator 可以有多个并行子任务(subtask),每个处理自己那一份数据流分片。那么问题来了:多个子任务发出的 Watermark,要怎么统一?

水位线的“取最小”原则

Flink 的 Watermark 合并机制是取所有并行子任务中最小的那个 Watermark,作为整个 Operator 的全局 Watermark。

这个设计虽然听起来“拖后腿”,但其实非常合理:只要有一个子任务的数据还没到齐,整个系统就不能贸然推进时间。

举个例子:

  • Subtask 0:Watermark = 08:00:10
  • Subtask 1:Watermark = 08:00:12
  • Subtask 2:Watermark = 08:00:08

那么整个 Operator 的有效 Watermark 就是 08:00:08。这就是所谓的“最小水位决定论”。

为什么不取最大、平均?

  • 取最大:会导致窗口过早触发,丢失延迟事件。
  • 取平均:没有实际意义,不能保证任何一个分区的数据都到齐。

所以,保守地取最小值是最安全的选择。

如何应对部分分区“卡住”不发水位的情况?

某些数据源可能因为没数据,Watermark 不更新,导致整个任务进度被阻塞。

解决办法:使用 withIdleness() 标记空闲输入流。

Waterma

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

17年+码农经历了很多次面试,多次作为面试官面试别人,多次大数据面试和面试别人,深知哪些面试题是会被经常问到。 在多家企业从0到1开发过离线数仓实时数仓等多个大型项目,详细介绍项目架构等企业内部秘不外传的资料,介绍踩过的坑和开发干货,分享多个拿来即用的大数据ETL工具,让小白用户快速入门并精通,指导如何入职后快速上手。 计划更新内容100篇以上,包括一些企业内部秘不外宣的干货,欢迎订阅!

全部评论
想入行数据分析师,是考CDA还是BDA啊
点赞 回复 分享
发布于 2025-07-01 23:32 湖南

相关推荐

2025-12-16 22:19
已编辑
南昌市第三中学 Java
个人背景:27届本科&nbsp;江西普通一本院校个人经历:小厂->用友->蔚来->美团->腾讯不知不觉已经有了五段实习经历,也快在外面漂泊一年半了,在今年也完成了两年前自己想进大厂的目标,可能在别人看来确实就是一段比较传奇的过程,一步一步都在向上走,也会有很多人来问我相关学习实习的一些问题,我看到了也会尽量去回复,但现在我想给大家说的并不是千篇一律的学习路线,而是我认为更为重要的——勇气与抉择。下面我来分享一下这些年的心路历程最初学习背景:我跟很多人一样,都是刚进入大学才开始接触计算机,也刚刚拥有自己的电脑,在刚开始学习的过程没有任何人来帮助我,给予我相关的指导,完全是自己摸索出来的一条学习路线,不会有如今这样有很多完善好的速成路线,而家里人都在想让我考研,似乎本科以我的学历就业是不现实的。我也很早意识到了学历对于我的限制,所以萌生出了大一就开始实习的想法,但这个想法在当时基本上是不存在。所有人都在抨击我(这里感兴趣的话可以看我最早发的帖子),有的人说本科想进大厂痴人说梦,有的人劝我以我的学历考研才是上策,有的人说我屁都不懂就来卷,总之我很难说去看到有支持的。我大一的时候还没卷成如今这样很多大一实习,当我想找到是否有跟我一样下定决心一步一步往上走的人,我当时是没有找到的,要么是秋招的哀嚎,要么就直接是零实习进大厂(现在我知道,这里所谓的普通学历0实习进大厂的水分有很多,排除真正意义上的运气和实力,其他基本上全是造假作弊,大家自己心知肚明,也要放平心态)这就导致了一个没有先例的情况,很多人也都是拿没有先例来抨击我,包括家里人也不支持我去实习,可能很多人的积极性就会下降,但我从来不会信所谓的不可能,如果没有先例,那我就会是第一个,他们不行,是因为他们没能力,他们坚持不下去。勇气是很重要的,当你发现你身边没有人像你一样,就很少会有人相信你,看好你,但好在,我不在乎。最初实习阶段:在最初3000沟通只有零星几个面试的时候,那感觉确实很不好受,沉没成本太大,得到的正反馈却太少,当时基本上都是一天学八个小时从来不间断,没有周末没有节假日,甚至过年我都在学习,这就导致我现在都会因为我周末偶尔休息的时候会有负罪感,我感觉已经是种病了,我也知道我也可以休息会但控制不了。当时我出去实习口袋里有1w块(这是我高中三年加大一一年存下来的,基本上是很抠很抠,一个月生活费有时候有一千多有时候就五六百,但也算得上是成功攒了一点钱)但第一次总会是很害怕,担心租房被骗,担心工作能力不行,担心被公司坑,担心学校原因导致不能实习等等,基本上在前面几段实习是根本不攒钱的,代课已经花了一万多,加上租房来回,基本上只能说堪堪不负支出,后来远赴北京,作为一个南方人,有很多不适应的地方,但现在回过头来一想,已经在北京呆了一年多了。我知道很多人要么担心学校因素,要么担心赚的还没花的多,种种因素导致了实习的困难,我也很害怕,我的钱会不会最终全部打水漂,学校会不会爆雷,我以后还能顺利实习吗等等。但对于我来说,我能对自己狠下心,我能接受通勤时间一个半小时只为节省那么几百块的房租钱,我能控制自己的消费的欲望,我能每个月大把大把把钱给代课,这可能就是我能够初期实习顺利的原因,这需要勇气,也需要对自己狠。实习中的抉择:在有了两段实习经历后,我的目标就朝着大厂进发,在去蔚来的中途,我oc了七八家中小厂公司,这里面不乏一些待遇极其优越的公司(有一家我真的差点就去了),但我最终还是都拒了,因为我清楚的明白想往上走的,只有公司title会帮你说话,没有人有义务理解你的困难你的坚持,好在最后去了蔚来,也算如愿以偿。从蔚来到美团倒是没有过多纠结,因为在最开始的梦中情厂就是美团,但从美团去腾讯这个决定或许是我人生中的转折点。美团多次挽留我,帮我沟通问hr,基本上就是一定能转暑期然后成功转正,仿佛这年薪40w的工作已经触手可得,所以在拿到腾讯offer的那一刻并没有多高兴,因为我意识到这可能是我此生最接近大厂的一次机会,可能大部分人都会选择留在美团,我也认为这一定是一个好的选择。我能够走到如今,是永远相信自己的判断,我的每一步都是在赌一个好的未来,只不过,这次赌注大了点而已,或许未来我再也进不了这些所谓的大厂,但我赌的不是选择错对,我赌我不后悔。所谓信念支撑:都说人要为自己而活,但我或许做不到,毕竟我身处人情社会,有许多爱我的人在等着我成长,我也不能接受因为能力而再次放弃一段感情,最近喜欢一段歌词:爱我的人相信我我一直在努力改变所有失败为你们而存在爱我的人感谢你你们的爱就算人生不是精彩我也要勇敢的姿态最后的最后,我想给大家传递的从来都不是一个普通学历进入大厂的意气风发,我想给大家传递的,是一股相信自己能够向上的信念和可能性。在没有打比赛能力,没有开源能力,没有学历等各个限制下,我帮大家试出了一条能够向上的路。如果没有先例,那我会是第一个。我们不需要弄虚作假,只靠自己一步一步脚踏实地,哪怕慢一点,不赌自己是否成功,只赌自己不后悔,问心无愧。最后送给大家,也送给自己一段话结束2025:生活可能没你想的那么好,也不会像你想的那么糟,人的脆弱和坚强,都超乎了你的想象,有时候可能脆弱的一句话就泪流满面,有时候你发现自己咬咬牙已经走了很长的路了
等闲_:感觉咱们双非的同学都有一个共性,想证明双非也是能进大厂的,我之前所有的标签都喜欢带着双非,仿佛这样可以像别人证明自己的实力,现在我却不再想证明双非到底能不能进大厂,我的生活的所有者是我自己,享受生活,接受结果
2025年终总结
点赞 评论 收藏
分享
评论
2
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务