FLINK SQL DDL中定义及使用watermark

5,519 阅读3分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

Flink 1.10 在 SQL DDL 中增加了针对流处理定义时间属性及产生 watermark 的语法扩展(FLIP-66 [22])。这使得用户可以在用 DDL 语句创建的表上进行基于时间的操作(例如窗口)以及定义 watermark 策略,该篇内容记录了流计算程序在创建kafak连接过程中使用watermark定义水印的实例以及解读。

watermark 概念

在Flink中,水位线是一种衡量Event Time进展的机制,用来处理实时数据中的乱序问题的,通常是水位线和窗口结合使用来实现。

从设备生成实时流事件,到Flink的source,再到多个oparator处理数据,过程中会受到网络延迟、背压等多种因素影响造成数据乱序。在进行窗口处理时,不可能无限期的等待延迟数据到达,当到达特定watermark时,认为在watermark之前的数据已经全部达到(即使后面还有延迟的数据), 可以触发窗口计算,这个机制就是 Watermark(水位线)

image.png 注:图源flink官方网站,侵删。

水位线的计算

watermark本质上是一个时间戳,且是动态变化的,会根据当前最大事件时间产生。watermarks具体计算为

watermark = 进入 Flink 窗口的最大的事件时间(maxEventTime)— 指定的延迟时间(t)

当watermark时间戳大于等于窗口结束时间时,意味着窗口结束,需要触发窗口计算。

DDL中的使用

CREATE TABLE Orders (
    `user` BIGINT,
    product STRING,
    order_time TIMESTAMP(3),
    # 定义水印
    WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (...);

WATERMARK子句定义表的事件时间属性并采用WATERMARK FOR rowtime_column_name AS watermark_strategy_expression这种格式。其中定义使用的字段是原表中中存在的并可以作为具有事件时间属性的字段(可以来源meta)。字段必须是表中的一级字段,是可以被计算的字段,并且该字段的类型必须是TIMESTAMP(3),水印计算表达的返回值类型也必须是TIMESTAMP(3)。

样例

create table table (
   `data` ARRAY<STRING>,
   `es` BIGINT,
   `old` ARRAY<MAP<STRING, STRING>>,
   `table` STRING,
   `type` STRING,
    proctime AS PROCTIME(),
    ts as TO_TIMESTAMP(FROM_UNIXTIME(`es`/1000,'yyyy-MM-dd HH:mm:ss')),
    WATERMARK FOR ts AS ts - INTERVAL '6' HOUR
) with (
   ...
);

watermark 策略

  • 严格意义上递增的时间戳,发出到目前为止已观察到的最大时间戳的水印。时间戳大于最大时间戳的行记录不会延迟计算。

      WATERMARK FOR rowtime_column AS rowtime_column
    
  • 递增的时间戳,发出到目前为止已观察到的最大时间戳-1毫秒的水印。时间戳等于或大于最大时间戳的行记录不会延迟计算。

      WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL '0.001' SECOND.
    
  • 有界时间戳(乱序) 发出水印,它是观察到的最大时间戳减去指定的延迟,例如,WATERMARK FOR rowtime_column AS rowtime_column-INTERVAL'5' SECOND是5秒的延迟水印策略。

      WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL 'string' timeUnit.
    

所以案例中的是6小时延迟策略。

需要注意:

Flink目前不支持直接把Long类型的转成Timestamp类型的,如果你的数据源中ts是Long类型的时间戳,建表语句不能直接写成ts TIMESTAMP(3),如果想用timestamp类型的,数据源中的时间需要时UTC格式的.或者可以用案例上面那种写法,利用Flink的日期函数TO_TIMESTAMP把bigint类型的转成timestamp类型的.