Flink 时间属性及 WATERMARK 水印

一、概述

        Apache Flink 是一个用于处理实时数据流的分布式计算框架,时间属性(Time Attributes)是 Flink 中非常重要的概念,用于处理时间相关的操作。Flink 支持三种主要的时间属性:

  1. 事件时间(Event Time)

    • 事件时间是事件在生成时的时间戳。它通常是由数据源(如传感器、日志)附带的。
    • 使用事件时间可以确保即使数据延迟到达,仍然可以按照事件生成的时间顺序进行处理。
    • 需要在数据流中提取时间戳,并可能需要处理迟到数据(late data)。
  2. 处理时间(Processing Time)

    • 处理时间是事件在 Flink 系统中被处理的时间,即系统的当前时间
    • 处理时间是最简单和最直接的时间属性,因为它不需要任何额外的时间戳提取或水印生成。
    • 适用于对延迟要求不高的场景,但可能会因为数据延迟或乱序而产生不准确的结果。

二、WATERMARK

2.1 基本概念

        在流处理系统中,水位线(Watermark)是一种机制,用于处理乱序数据。它表示数据流中的事件时间进度。具体来说,水位线是一个时间戳,表示系统认为在这个时间戳之前的所有事件都已经到达

        在实际的流数据处理中,事件可能会乱序到达。例如,网络延迟、系统故障等原因都可能导致事件不能按时间顺序到达。如果不处理这些乱序事件,可能会导致不准确的计算结果。水位线可以帮助系统处理这些乱序事件,确保窗口计算的正确性。

        当水位线到达某个时间点时,系统会触发相应时间窗口的计算,并认为在这个时间点之前的事件不再会到达。在 Flink SQL 中,可以通过定义水位线策略来生成水位线。通常使用 WATERMARK FOR 语句来定义。

        水位线是 Flink 处理乱序数据的重要机制。通过定义水位线,Flink 可以在处理时间和事件时间之间建立联系,确保窗口计算的准确性。你可以在窗口计算中使用它们来处理乱序和迟到数据。

2.2 水位线的生成原理

水位线的生成通常基于以下几个原则:

  1. 最大延迟假设: 假设数据可能会乱序到达,但这种乱序有一个最大延迟。例如,假设数据最多会延迟5秒到达。
  2. 递增性: 水位线的时间戳必须是单调递增的,不能回退。这意味着一旦水位线时间戳前进到某个时间点,就不会再回到之前的时间点。
  3. 基于事件时间的推进: 水位线的时间戳通常是基于事件时间的推进。例如,当前最大的事件时间减去一个固定的延迟时间。

2.3 具体实现

在 Flink 中,水位线的生成可以通过以下几种方式:

  1. 周期性生成水位线: 每隔一段时间生成一个新的水位线。
  2. 基于事件的生成水位线: 每收到一个事件就更新一次水位线。

下面展示一个case,假设我们有一组事件,它们的事件时间如下:

事件1: 2024-08-03 01:00:00
事件2: 2024-08-03 01:00:02
事件3: 2024-08-03 01:00:05
事件4: 2024-08-03 01:00:01

我们假设最大延迟时间为5秒。下面是水位线的生成过程:

  1. 初始状态: 没有事件到达,水位线为负无穷。
  2. 事件1到达: 事件时间为 2024-08-03 01:00:00,水位线更新为 2024-08-03 00:59:55(事件时间减去5秒)。
  3. 事件2到达: 事件时间为 2024-08-03 01:00:02,水位线更新为 2024-08-03 00:59:57(事件时间减去5秒)。
  4. 事件3到达: 事件时间为 2024-08-03 01:00:05,水位线更新为 2024-08-03 01:00:00(事件时间减去5秒)。
  5. 事件4到达: 事件时间为 2024-08-03 01:00:01,由于水位线必须是单调递增的,所以水位线保持不变,仍为 2024-08-03 01:00:00

2.4 总结

        水位线的生成是一个基于事件时间的机制,主要目的是处理乱序数据,确保窗口计算的准确性。通过设定一个最大延迟时间,水位线可以有效地处理乱序事件,并在适当的时间点触发窗口计算。

三、时间属性的设置

3.1 事件时间

        事件时间是事件在生成时的时间戳。Flink SQL 允许你在表定义中指定事件时间属性,并通过 WATERMARK 语句来定义水印策略,以处理乱序和迟到数据。

CREATE TEMPORARY TABLE src_kafka (
`create_time` TIMESTAMP(3)  NOT NULL METADATA from 'timestamp',
`offset`    bigINT NOT NULL METADATA from 'offset' ,
`partition` INT NOT NULL METADATA from 'partition' ,
 cs1 STRING,
 ip STRING,
 b STRING,
 t STRING,
 n STRING,
 tm BIGINT,
 event_time as TO_TIMESTAMP(tm),
 WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND ,
 PRIMARY KEY (`partition`, `offset`) NOT ENFORCED
) WITH (
  'connector' = 'kafka',
  'topic' = 'ai_log',
  'properties.bootstrap.servers' = 'alikafka',
  'properties.group.id' = 'pyspark-consumer',
   'scan.startup.mode' = 'latest-offset', 
  'format' = 'json'
);


SELECT
*
from src_kafka 
where n = 'ADCompanyShow' 
and cs1 is not null 
;

        在上面的示例中,event_time 被定义为事件时间,并且通过 WATERMARK 语句指定了水印策略,允许 5 秒的最大乱序。

        请注意:定义水印并不会改变列本身的值。水印的作用是帮助 Flink 处理乱序数据和触发窗口计算,而 event_time 列仍然保持其原始的时间戳值。

3.2 处理时间

        处理时间是事件在 Flink 系统中被处理的时间。处理时间属性可以在表定义中直接指定。

CREATE TEMPORARY TABLE src_kafka (
`create_time` TIMESTAMP(3)  NOT NULL METADATA from 'timestamp',
`offset`    bigINT NOT NULL METADATA from 'offset' ,
`partition` INT NOT NULL METADATA from 'partition' ,
 cs1 STRING,
 ip STRING,
 b STRING,
 t STRING,
 n STRING,
 tm BIGINT,
 event_time as TO_TIMESTAMP(tm),
proctm AS PROCTIME() ,
 PRIMARY KEY (`partition`, `offset`) NOT ENFORCED
) WITH (
  'connector' = 'kafka',
  'topic' = 'ai_log',
  'properties.bootstrap.servers' = 'alikafka',
  'properties.group.id' = 'pyspark-consumer',
   'scan.startup.mode' = 'latest-offset',
  'format' = 'json'
);


SELECT
`create_time` ,
`offset`    ,
`partition`  ,
cs1 ,
ip ,
b ,
t ,
n ,
tm ,
event_time,
proctm + interval '8' hour
from src_kafka 
where n = 'ADCompanyShow' 
and cs1 is not null 
;

        此篇内容中所讲述的时间属性以及水印在需求中如何使用,后续将在窗口计算那一章详细讲解,敬请期待。

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值