对时间的处理

用流处理器编程和用批处理器编程最关键的区别在于对时间的处理。
《Flink基础教程》。

采用批处理架构和Lambda架构计数

 用定期运行的批处理作业来实现应用程序的持续性。
 数据被持续地分割为文件(如以一小时为单位);
 然后,批处理作业将文件作为输入,以此达到持续处理数据的效果。

 Lambda架构用定期运行的批处理作业来实现应用程序的持续性,并通过流处理器获得预警。
 流处理器实时提供近似结果;
 批处理层最终会对近似结果予以纠正。

image

 这种架构完全可行,但是存在以下问题:

  1. 太多独立的部分-系统多,学习和管理成本,BUG。
  2. 预警…近似实时与定时批处理结合。
  3. 乱序事件流。
  4. 批处理作业的界限不清晰。

采用流处理架构计数

 通过流处理架构实现应用程序的持续性。
 水平圆柱体表示消息传输系统 (Kafka或MapR Streams)。
 消息传输系统为负责处理所有数据的流处理器提供流数据,产生的结果既是实时的,也是正确的。

image

 Flink作业的速度减慢或者吞吐量激增只会导致事件在消息传输系统中堆积。
 以时间为单位把事件流分割为一批批任务(称作窗口),这种逻辑完全嵌入在Flink程序的应用逻辑中。
 预警由同一个程序生成,乱序事件由Flink自行处理。
 要从以固定时间分组改为根据产生数据的时间段分组,只需在Flink程序中修改对窗口的定义即可。
 如果应用程序的代码有过改动,只需重播Kafka的topic,即可重播应用程序[新的消费者组重新消费计算]。

 流处理区别于批处理最主要的两点是:
流即是流,不必人为地将它分割为文件;
时间的定义被明确地写入应用程序代码(如以上代码的时间窗口),而不是与摄取、计算和调度等过程牵扯不清。

流处理系统中的批处理

 微批处理:介于流处理和批处理之间的方法。

 流处理系统中的批处理必须符合以下两点要求:
批处理只作为提高系统性能的机制。批量越大,系统的吞吐量就越大。
为了提高性能而使用的批处理必须完全独立于定义窗口时所用的缓冲,或者为了保证容错性而提交的代码,也不能作为API的一部分。
否则,系统将受到限制,并且变得脆弱且难以使用【?】。

时间概念

 事件时间,即事件实际发生的时间。
 更准确地说,每一个事件都有一个与它相关的时间戳,并且时间戳是数据记录的一部分(比如手机或者服务器的记录)。
 事件时间其实就是时间戳。
 处理时间,即事件被处理的时间。
 处理时间其实就是处理事件的机器所测量的时间。

 在现实世界中,许多因素(如连接暂时中断,不同原因导致的网络延迟,分布式系统中的时钟不同步,数据速率陡增,物理原因,或者运气差)使得事件时间和处理时间存在偏差(即事件时间偏差)。
 事件时间顺序和处理时间顺序通常不一致,这意味着事件以乱序到达流处理器。

 根据应用程序的不同,两个时间概念都很有用。
 有些应用程序(如一些预警应用程序)需要尽可能快地得到结果,即使有小的误差也没关系。
 它们不必等待迟到的事件,因此适合采用处理时间语义。

 其他一些应用程序(如欺诈检测系统或者账单系统)则对准确性有要求:
 只有在时间窗口内发生的事件才能被算进来。
 对于这些应用程序来说,事件时间语义才是正确的选择。

 也有两者都采用的情况,比如既要准确地计数,又要提供异常预警。

窗口

 窗口是一种机制,它用于将许多事件按照时间或者其他特征分组,从而将每一组作为整体进行分析(比如求和)。

时间窗口

 时间窗口是最简单和最有用的一种窗口。它支持滚动和滑动。

 一分钟滚动窗口收集最近一分钟的数值,并在一分钟结束时输出总和。

image

 一分钟滑动窗口计算最近一分钟的数值总和,但每半分钟滑动一次并输出结果。

image

计数窗口

 采用计数窗口时,分组依据不再是时间戳,而是元素的数量。
 虽然计数窗口有用,但是其定义不如时间窗口严谨,因此要谨慎使用。

会话窗口

 会话指的是活动阶段,其前后都是非活动阶段,例如用户与网站进行一系列交互(活动阶段)之后,关闭浏览器或者不再交互(非活动阶段)。
 会话需要有自己的处理机制,因为它们通常没有固定的持续时间(有些30秒就结束了,有些则长达一小时),或者没有固定的交互次数(有些可能是3次点击后购买,另一些可能是40次点击却没有购买)。

触发器

 !!!很重要!!!
 触发器控制生成结果的时间,即何时聚合窗口内容并将结果返回给用户。
 每一个默认窗口都有一个触发器。

 例如,采用事件时间的时间窗口将在收到水印时被触发【然而事实并非如此】。
 对于用户来说,除了收到水印时生成完整、准确的结果之外,也可以实现自定义的触发器(例如每秒提供一次近似结果)。

窗口的实现

 在Flink内部,所有类型的窗口都由同一种机制实现。实现细节需要注意:

 开窗机制与检查点机制完全分离。这意味着窗口时长不依赖于检查点间隔。
 事实上,窗口完全可以没有”时长”(比如上文中的计数窗口和会话窗口的例子)。
 高级用户可以直接用基本的开窗机制定义更复杂的窗口形式(如某种时间窗口,它可以基于计数结果或某一条记录的值生成中间结果)。

时空穿梭

 如何处理历史数据?
 如何重新处理数据呢?

 时空穿梭意味着将数据流倒回至过去的某个时间,重新启动处理程序,直到处理至当前时间为止。
 像Kafka和MapRStreams这样的现代传输层,支持时空穿梭,这使得它们与更早的解决方案有所区别。
 实时流处理总是在处理最近的数据,历史流处理则从过去开始,并且可以一直处理至当前时间。
 若要按时间回溯并正确地重新处理数据,流处理器必须支持 事件时间。
 如果窗口的设定是根据系统时间而不是时间戳,那么每次运行同样的程序,都会得到不同的结果。
 事件时间使数据处理结果具有确定性,因为用同一组数据运行同样的程序,会得到相同的结果。

image

水印(水位线)

 支持事件时间对于流处理架构而言至关重要,因为事件时间能保证结果正确,并使流处理架构拥有重新处理数据的能力。
 当计算基于事件时间时,如何判断所有事件是否都到达,以及何时计算和输出窗口的结果呢?
 如何追踪事件时间,并知晓输入数据已经流到某个事件时间了呢?
 为了追踪事件时间,需要依靠由数据驱动的时钟,而不是系统时钟。
 Flink通过水印来推进事件时间。
 水印是嵌在流中的常规记录,计算程序通 过水印获知某个时间点已到。
 对于一分钟滚动窗口,假设水印标记时间为10:01:00(或者其他时间,如10:03:43),那么收到水印的窗口就知道不会再有早于该时间的记录出现,因为所有时间戳小于或等于该时间的事件都已经到达。
 这时,窗口可以安全地计算并给出结果(总和)。水印使事件时间与处理时间完全无关。
 迟到的水印(“迟到”是从处理时间的角度而 言)并不会影响结果的正确性,而只会影响收到结果的速度。

 !!!事实就是使用事件时间时,时间窗口的结果是非实时的,如果数据流数据之间事件时间间隔大,已经不适合使用Flink了。

水印是如何生成的

 在Flink中,水印由应用程序开发人员生成,这通常需要对相应的领域有一定的了解。
 完美的水印永远不会错:时间戳小于水印标记时间的事件不会再出现。
 在特殊情况下(例如非乱序事件流),最近一次事件的时间戳就可能是完美的水印。
 启发式水印则相反,它只估计时间,因此有可能出错,即迟到的事件(其时间戳小于水印标记时间)晚于水印出现。
 针对启发式水印,Flink提供了处理迟到元素的机制。
 设定水印通常需要用到领域知识。
 举例来说,如果知道事件的迟到时间不会超过5秒,就可以将水印标记时间设为收到的最大时间戳减去5秒。
 另一种做法是,采用一个Flink作业监控事件流,学习事件的迟到规律,并以此构建水印生成模型。

案例架构图

image
image

【完】

GitHub flink-training-exercises
GitHub sql-training
本文知识来源于书本,仅供快速浏览学习,勿盲目转载
GitHub flink-training-course/课程表(持续更新)

邵志鹏 wechat
扫一扫上面的二维码关注我的公众号
0%