首页/数据/数据工程与 ETL 速查/水位线 Watermark 与乱序容忍
管道基础52 条

水位线 Watermark 与乱序容忍

水位线是事件时间已推进到某一点的标记:引擎收到水位线后才认为更早的数据基本到齐,从而触发窗口计算;允许延迟越大结果越准,输出越晚。

水位线是什么

水位线(Watermark)是流处理引擎用来判断「事件时间已经推进到哪一点」的标记。引擎收到某个时刻的水位线后,就认为比它更早的数据基本已经到齐,于是触发对应的窗口计算。它的本质是一个关于完整性的假设,而不是事实。

为什么需要乱序容忍

数据从产生到进入引擎要经过网络、缓冲与多个环节,同一秒产生的事件可能相隔数秒才陆续到达。如果一收到数据就立刻计算,窗口结果会随迟到数据不断变化。水位线用「允许迟到多少时间」换取结果稳定:延迟设得越大,统计越准,等待越久、状态越多。

基本信息

作用界定窗口何时可以触发计算
生成方式事件时间字段减去固定延迟,也可自定义生成逻辑
单调性水位线只增不减,迟到数据不会让已发出的水位线回退
空闲分区长时间无数据的分区会拖住整体水位线,需设置 idle 超时
相关问题允许延迟越大,窗口保持越久,checkpoint 体积越大

三个概念的关系

概念含义对结果的影响
事件时间数据自带的发生时刻可复现,对外指标优先用它
水位线认为更早数据基本到齐的标记决定窗口何时触发输出
允许迟到水位线相对事件时间落后的幅度越大越准,但输出越晚、状态越大

典型使用场景

  • 按分钟统计订单量:用事件时间加水位线,避免边界数据漏算
  • 跨系统对账:允许迟到数十秒,让上游网络抖动不至于造成金额差异
  • 监控实时延迟:窗口延迟设小,先保证告警及时
  • 报表类流任务:宁可多等一分钟,也要把迟到数据收进正确窗口
  • 排查「结果比源端少」的问题:先确认水位线是否推得太快

Flink SQL 声明水位线

Flink SQL 声明水位线
-- 建表时用 WATERMARK 子句声明:事件时间字段减去允许延迟
CREATE TABLE orders_kafka (
  order_id STRING,
  amount   DECIMAL(10,2),
  ts       TIMESTAMP(3),
  WATERMARK FOR ts AS ts - INTERVAL '10' SECOND   -- 容忍 10 秒乱序
) WITH ('connector' = 'kafka', 'topic' = 'orders', 'format' = 'json');

-- 窗口要等水位线越过窗口结束时间才触发输出
SELECT TUMBLE_START(ts, INTERVAL '1' MINUTE) AS w, SUM(amount)
FROM orders_kafka
GROUP BY TUMBLE(ts, INTERVAL '1' MINUTE);

迟到数据的两种处理

迟到数据的两种处理
# 办法一:把允许延迟调大,窗口多等一会儿
#   代价:窗口在状态里保留更久,checkpoint 变大,输出更晚

# 办法二:丢弃但旁路输出,把迟到数据单独写一个侧流
#   用途:既能知道上游延迟有多严重,又不影响主链路口径

# 排查经验:源端时间戳时区不统一(本地时间混 UTC)会让窗口整体错位,先统一约定再调参数
水位线表达式写成 ts 加延迟(而不是减)会让窗口提前触发,输出数字系统性偏低,且不会报错,是流任务里最难发现的错误之一。
多分区输入时,整体水位线取各分区的最小值:某个分区长期没有数据就不会推进水位线,窗口随之不触发。这种情况要靠空闲超时(idle timeout)或定期发心跳解决。

易错点

  • 某个输入分区长期无数据卡住整体水位线,窗口长时间不触发,需要配置空闲分区超时
  • 水位线表达式写成事件时间加延迟,窗口提前触发,统计结果偏低且没有报错提示
  • 允许延迟调得过大而不治理状态,重启恢复时间随 checkpoint 体积一起膨胀
  • 源端时间戳时区不统一(本地时间与 UTC 混用),窗口错位,表现为数据集中在相邻两个窗口

数据来源与参考

  • Apache Flink 官方文档:Generating Watermarks
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌