水位线是什么
水位线(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)或定期发心跳解决。