【大数据】Flink 中的事件时间处理

Flink 中的事件时间处理

  • 1.时间戳
  • 2.水位线
  • 3.水位线传播和事件时间
  • 4.时间戳分配和水位线生成

在之前的博客中,我们强调了时间语义对于流处理应用的重要性并解释了 处理时间事件时间 的差异。虽然处理时间是基于处理机器的本地时间,相对容易理解,但它会产生一些较为随意、不一致且无法重现的结果。相反,事件时间语义会生成可重现且一致性的结果,这也是很多流处理用例的刚性需求。但和基于处理时间语义的应用相比,基于事件时间的应用需要一些额外的配置。此外,相比纯粹使用处理时间的引擎,支持事件时间的流处理引擎内部要更加复杂。

Flink 不仅针对常见的事件时间操作提供了直观易用的原语,还支持一些表达能力很强的 API,允许使用者以自定义算子的方式实现更高级的事件时间处理应用。在面对这些高级应用时,充分理解 Flink 内部事件处理机制通常会有所帮助,有时候更是必要的。在《流处理基础概念(二):时间语义(处理时间、事件时间、水位线)》一文中,我们介绍了 Flink 在提供处理时间语义时所采用的两个概念:记录时间戳水位线。接下面我们会介绍 Flink 内部如何实现和处理时间戳及水位线以支持事件时间语义的流式应用。

1.时间戳

在事件时间模式下,Flink 流式应用处理的所有记录都必须包含时间戳。时间戳将记录和特定时间点进行关联,这些时间点通常是记录所对应事件的发生时间。但实际上应用可以自由选择时间戳的含义,只要保证流记录的时间戳会随着数据流的前进大致递增即可。正如前文所述,基本上所有现实应用场景都会出现一定程度的时间戳乱序。

当 Flink 以事件时间模式处理数据流时,会根据记录的时间戳触发时间相关算子的计算。例如,时间窗口算子会根据记录关联的时间戳将其分配到窗口中。Flink 内部采用 8 字节的 Long 值对时间戳进行编码,并将它们以元数据(metadata)的形式附加在记录上。内置算子会将这个 Long 值解析为毫秒精度的 Unix 时间戳(自 1970-01-01-00:00:00.000 以来的毫秒数)。但自定义算子可以有自己的时间戳解析机制,如将精度调整为微秒。

2.水位线

除了记录的时间戳,Flink 基于事件时间的应用还必须提供 水位线watermark)。水位线用于在事件时间应用中推断每个任务当前的事件时间。基于时间的算子会使用这个时间来触发计算并推动进度前进。例如:基于时间窗口的任务会在其事件时间超过窗口结束边界时进行最终的窗口计算并发出结果。

当一个算子接收到时间为 T 的水位线,就可以认为不会再收到任何时间戳小于或等于 T 的事件了。水位线无论对于事件时间窗口还是处理乱序事件的算子都很关键。算子一旦收到某个水位线,就相当于接收到信号:某个特定时间区间的时间戳已经到齐,可以触发窗口计算或对接收的数据进行排序了
在这里插入图片描述
在 Flink 中,水位线是利用一些包含 Long 值时间戳的特殊记录来实现的。如上图所示,它们像带有额外时间戳的常规记录一样在数据流中移动。

水位线拥有两个基本属性:

  • 必须单调递增。这是为了确保任务中的事件时间时钟正确前进,不会倒退。
  • 和记录的时间戳存在联系。一个时间戳为 T 的水位线表示,接下来所有记录的时间戳一定都大于 T。

第二个属性可用来处理数据流中时间戳乱序的记录,例如上图中的时间戳为 3 和 5 的记录。对基于时间的算子任务而言,其收集和处理的记录可能会包含乱序的时间戳。这些算子只有当自己的事件时间时钟(由接收的水位线驱动)指示不必再等那些包含相关时间戳的记录时,才会最终触发计算。当任务收到一个违反水位线属性,即时间戳小于或等于前一个水位线的记录时,该记录本应参与的计算可能已经完成。我们称此类记录为 迟到记录late record)。为了处理迟到记录,Flink 提供了不同的机制,我们将在后续讨论它们。

水位线的意义之一在于它允许应用控制结果的完整性和延迟。如果水位线和记录的时间戳非常接近,那结果的处理延迟就会很低,因为任务无须等待过多记录就可以触发最终计算。但同时结果的完整性可能会受影响,因为可能有部分相关记录被视为迟到记录,没能参与运算。相反,非常 “保守” 的水位线会增加处理延迟,但同时结果的完整性也会有所提升。

3.水位线传播和事件时间

Flink 内部将水位线实现为特殊的记录,它们可以通过算子任务进行接收和发送。任务内部的 时间服务time service)会维护一些 计时器timer),它们依靠接收到水位线来激活。这些计时器是由任务在时间服务内注册,并在将来的某个时间点执行计算。例如:窗口算子会为每个活动窗口注册一个计时器,它们会在事件时间超过窗口的结束时间时清理窗口状态。

当任务接收到一个水位线时会执行以下操作:

  • 基于水位线记录的时间戳更新内部事件时间时钟。
  • 任务的时间服务会找出所有触发时间小于更新后事件时间的计时器。对于每个到期的计时器,调用回调函数,利用它来执行计算或发出记录。
  • 任务根据更新后的事件时间将水位线发出。

Flink 对通过 DataStream API 访问时间戳和水位线有一定限制。普通函数无法读写记录的时间戳或水位线,但一系列处理函数(process function)除外。它们可以读取当前正在处理记录的时间戳,获得当前算子的事件时间,还能注册计时器。所有函数的 API 都无法支持设置发出记录的时间戳、调整任务的事件时间时钟或发出水位线。为发出记录配置时间戳的工作需要由基于时间的 DataStream 算子任务来完成,这样才能确保时间戳和发出的水位线对齐。举例而言,时间窗口算子任务会在发送触发窗口计算的水位线时间戳之前,将所有经过窗口计算所得结果的时间戳设为窗口的结束时间。

接下来我们详细解释一下任务在收到一个新的水位线之后,将如何发送水位线和更新其内部事件时间时钟。Flink 会将数据流划分为不同的分区,并将它们交由不同的算子任务来并行执行。每个分区作为一个数据流,都会包含带有时间戳的记录以及水位线。根据算子的上下游连接情况,其任务可能需要同时接收来自多个输入分区的记录和水位线,也可能需要将它们发送到多个输出分区。下面我们将详细介绍一个任务如何将水位线发送至多个输出任务,以及它从多个输入任务获取水位线后如何推动事件时间时钟前进。

一个任务会为它的每个输入分区都维护一个 分区水位线partition watermark)。当收到某个分区传来的水位线后,任务会以接收值和当前值中较大的那个去更新对应分区水位线的值。随后,任务会把事件时间时钟调整为所有分区水位线中最小的那个值。如果事件时间时钟向前推动,任务会先处理因此而触发的所有计时器,之后才会把对应的水位线发往所有连接的输出分区,以实现事件时间到全部下游任务的广播。

下图展示了一个有 4 个输入分区和 3 个输出分区的任务在接收到水位线后,是如何更新它的分区水位线和事件时间时钟,并将水位线发出的。

在这里插入图片描述
对于那些有着两条或多条输入数据流的算子,如 UnionCoFlatMap,它们的任务同样是利用全部分区水位线中的最小值来计算事件时间时钟,并没有考虑分区是否来自不同的输入流。这就导致所有输入的记录都必须基于同一个事件时间时钟来处理。如果不同输入流的事件时间没有对齐,那么该行为就会导致一些问题。

Flink 的水位线处理和传播算法保证了算子任务所发出的记录时间戳和水位线一定会对齐。然而,这依赖于一个事实:所有分区都会持续提供自增的水位线。只要有一个分区的水位线没有前进,或分区完全空闲下来不再发送任何记录或水位线,任务的事件时间时钟就不会前进,继而导致计时器无法触发。这种情形会给那些靠时钟前进来执行计算或清除状态的时间相关算子带来麻烦。因此,如果一个任务没有从全部输入任务以常规间隔接收新的水位线,就会导致时间相关算子的处理延迟或状态大小激增。

当算子两个输入流的水位线差距很大时,也会产生类似影响。对于一个有两个输入流的任务而言,其事件时间时钟会受制于那个相对较慢的流,而较快流的记录或中间结果会在状态中缓冲,直到事件时间时钟到达允许处理它们的那个点。

4.时间戳分配和水位线生成

到目前为止,我们已经解释了时间戳和水位线的含义以及它们在 Flink 内部的处理逻辑,但一直没涉及它们的来源。时间戳和水位线通常都是在数据流刚刚进入流处理应用的时候分配和生成的。由于不同的应用会选择不同的时间戳,而水位线依赖于时间戳和数据流本身的特征,所以应用必须显式地分配时间戳和生成水位线。Flink DataStream 应用可以通过三种方式完成该工作:

  • 在数据源完成:我们可以利用 SourceFunction 在应用读入数据流的时候分配时间戳和生成水位线。源函数会发出一条记录流。每个发出的记录都可以附加一个时间戳,水位线可以作为特殊记录在任何时间点发出。如果源函数(临时性地)不再发出水位线,可以把自己声明成空闲。Flink 会在后续算子计算水位线的时候把那些来自于空闲源函数的流分区排除在外。数据源空闲声明机制 可以用来解决上面提到的水位线不向前推进的问题。我们会在后续详细讨论数据源函数。
  • 周期分配器periodic assigner):DataStream API 提供了一个名为 AssignerWithPeriodicWatermarks 的用户自定义函数,它可以用来从每条记录提取时间戳,并周期性地响应获取当前水位线的查询请求。提取出来的时间戳会附加到各自的记录上,查询得到的水位线会注入到数据流中。这个函数会在后续介绍。
  • 定点分配器punctuated assigner):另一个支持从记录中提取时间戳的用户自定义函数叫作 AssignerWithPunctuatedWatermarks。它可用于需要根据特殊输入记录生成水位线的情况。和 AssignerWithPeriodicWatermarks 函数不同,这个函数不会强制你从每条记录中都提取一个时间戳(虽然这样也行)。这个函数也会在后续介绍。

用户自定义的时间戳分配函数通常都会尽可能地靠近数据源算子,因为在经过其他算子处理后,记录顺序和它们的时间戳会变得难以推断。这也是为什么不建议在流式应用中途覆盖已有的时间戳和水位线(虽然这可以通过用户自定义函数实现)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351123.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

再谈Android View绘制流程

一,先思考何时开始绘制 笔者在这里提醒读者,Android的View是UI的高级抽象,我们平时使用的XML文件也好,本质是设计模式中的一种策略模式,其View可以理解为一种底层UI显示的Request。各种VIew的排布,来自于开…

scrapy的概念作用和工作流程

1. scrapy的概念 Scrapy是一个Python编写的开源网络爬虫框架。它是一个被设计用于爬取网络数据、提取结构性数据的框架。 Scrapy 使用了Twisted[twɪstɪd]异步网络框架,可以加快我们的下载速度。 Scrapy文档地址:http://scrapy-chs.readthedocs.io/zh_…

pytorch 实现中文文本分类

🍨 本文为[🔗365天深度学习训练营学习记录博客🍦 参考文章:365天深度学习训练营🍖 原作者:[K同学啊 | 接辅导、项目定制]\n🚀 文章来源:[K同学的学习圈子](https://www.yuque.com/mi…

K8s 安装部署-Master和Minion(Node)文档

K8s 安装部署-Master和Minion(Node)文档 操作系统版本:CentOS 7.4 Master :172.20.26.167 Minion-1:172.20.26.198 Minion-2:172.20.26.210(后增加节点) ETCD:172.20.27.218 先安装部署ETC…

ElasticSearch的集群管理命令

ElasticSearch版本 {"name" : "data-slave1","cluster_name" : "data-es","cluster_uuid" : "xxxxxxxxxx-eMwxw","version" : {"number" : "7.2.1","build_flavor" : &…

【JaveWeb教程】(32)SpringBootWeb案例之《智能学习辅助系统》的详细实现步骤与代码示例(5)文件上传的实现

目录 SpringBootWeb案例052. 文件上传2.1 简介2.2 本地存储 SpringBootWeb案例05 前面我们已经实现了员工信息的条件分页查询以及删除操作。 关于员工管理的功能,还有两个需要实现新增和修改员工。 本节的主要内容: 文件上传 2. 文件上传 在我们完成…

【开源】基于JAVA语言的实验室耗材管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 耗材档案模块2.2 耗材入库模块2.3 耗材出库模块2.4 耗材申请模块2.5 耗材审核模块 三、系统展示四、核心代码4.1 查询耗材品类4.2 查询资产出库清单4.3 资产出库4.4 查询入库单4.5 资产入库 五、免责说明 一、摘要 1.1…

docker 存储管理

文章目录 docker 存储管理容器存储方案docker 容器存储解决方案 docker 存储驱动基本概述存储驱动的选择原则主流的 docker 存储驱动docker 版本支持的存储驱动 overlay2 存储驱动OverlayFSoverlay2 存储驱动要求配置 docker 使用 overlay2 驱动 overlay2 存储驱动的工作机制Ov…

C++核心编程:C++ 中的引用 笔记

2.引用 2.1 引用的基本使用 - 作用&#xff1a;给变量起别名 - 语法&#xff1a;数据类型 &别名 原名 #include<iostream> using namespace std; int main() {// 引用基本语法// 数据类型 &别名 原名int a 10;// 创建引用int &ref_a a;cout<<&qu…

搭建Redis集群

一 应用场景 为什么需要redis集群&#xff1f; 当主备复制场景&#xff0c;无法满足主机的单点故障时&#xff0c;需要引入集群配置。 一般数据库要处理的读请求远大于写请求 &#xff0c;针对这种情况&#xff0c;我们优化数据库可以采用读写分离的策略。我们可以部 署一台…

Ubuntu 系统如何修改时间

Ubuntu 系统如何修改时间 简介&#xff1a;在Ubuntu上&#xff0c;你可以使用以下三种方法来修改时间&#xff1a;date命令&#xff0c;timedatectl命令和hw 原文&#xff1a;Ubuntu 系统如何修改时间 (baidu.com)https://cloud.baidu.com/article/393621 三种方法修改Ubunt…

Linux——文本编辑器Vim

Linux中的所有内容以文件形式管理&#xff0c;在命令行下更改文件内容&#xff0c;常常会用到文本编辑器。我们首选的文本编辑器是Vim&#xff0c;它是一个基于文本界面的编辑工具&#xff0c;使用简单且功能强大&#xff0c;更重要的是&#xff0c;Vim是所有Linux发行版本的默…

husky结合commitlint审查commit信息

commintlint是一个npm包用来规范化我们的commit信息&#xff0c;当然这个行为的操作时期是在git的commit-msg生命周期期间&#xff0c;这一点当然是有husky来控制&#xff0c;需要注意的是commit-msg作为一个git生命周期会被git commit和git merge行为唤醒&#xff0c;并且可以…

03-Redis缓存高可用集群

文章目录 1、Redis集群方案比较2、Redis高可用集群搭建redis集群搭建Java操作redis集群 4、Redis集群原理分析槽位定位算法跳转重定位Redis集群节点间的通信机制gossip通信的10000端口网络抖动 Redis集群选举原理分析集群脑裂数据丢失问题集群是否完整才能对外提供服务Redis集群…

第二百八十八回

文章目录 1. 概念介绍2. 使用方法2.1 实现步骤2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取文件类型"相关的内容&#xff0c;本章回中将介绍如何播放视频.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 播放视频是我们常用…

用Visual Studio Code创建JavaScript运行环境【2024版】

用Visual Studio Code创建JavaScript运行环境 JavaScript 的历史 JavaScript 最初被称为 LiveScript&#xff0c;由 Netscape&#xff08;Netscape Communications Corporation&#xff0c;网景通信公司&#xff09;公司的布兰登艾奇&#xff08;Brendan Eich&#xff09;在 …

Java基础—面向对象OOP—17类与对象(创建、构造器、创建对象时简单内存分析)

把握重点&#xff0c;重点已标注&#xff0c;这篇笔记分了4个章节&#xff0c;重点看二、三、四 一、整体思维--重点把握面向对象的本质和特点 1、面向对象编程OOP&#xff1a; Object-Oriented programming 2、面向过程与面向对象 面向过程&#xff1a;线性思维 面向对象…

[嵌入式软件][启蒙篇][仿真平台] STM32F103实现IIC控制OLED屏幕

上一篇&#xff1a;[嵌入式软件][启蒙篇][仿真平台] STM32F103实现LED、按键 [嵌入式软件][启蒙篇][仿真平台] STM32F103实现串口输出输入、ADC采集 [嵌入式软件][启蒙篇][仿真平台]STM32F103实现定时器 [嵌入式软件][启蒙篇][仿真平台] STM32F103实现IIC控制OLED屏幕 文章目…

有关链表的题目

目录 1.环形链表的约瑟夫问题 2.链表的中间节点 3.合并两个有序链表 4.反转链表 5.移除链表元素 1.环形链表的约瑟夫问题 环形链表的约瑟夫问题_牛客题霸_牛客网 (nowcoder.com) 思路&#xff1a;题目给出结构是环形链表&#xff0c;且题目已经定义好了环形链表的结构。 1…

MATLAB - 控制小车上的倒立摆

系列文章目录 前言 一、小车 - 摆杆 小车 - 摆杆模型如图 1 所示&#xff0c;使用 Simscape™ Multibody™ 在 Simulink 中建模。 图 1&#xff1a;小车上的倒立摆 图 2&#xff1a;Simscape 多体模型 该系统通过对小车施加可变力 进行控制。控制器需要在将小车移动到新位置或…