大数据实时流计算的状态管理:流数据状态和流信息状态!

流的状态

关联操作中临时保存的窗口数据、实现时间维度聚合特征、关联图谱特征、CEP中有限状态机、统计或机器学习模型的参数估计,实时流计算系统需要的几个主要计算目标无不与“状态”有关。需要注意的是,这些状态是有区别的。

我们将流在执行过程中涉及的状态分为两类:流数据状态和流信息状态。

这两个概念是由笔者在本文中第一次提出的,所以读者们若在其他地方看到类似概念定义的话,一定纯属巧合。

·流数据状态。

在流数据处理的过程中,可能需要处理事件窗口、时间乱序、多流关联等问题,在解决这些问题的过程中,通常会涉及对部分流数据的临时缓存,并在处理完后将其清理。我们将临时保存的部分流数据称为流数据状态。

·流信息状态。

在对流数据的分析过程中,我们会得到一些感兴趣的信息,如时间维度的聚合数据、关联图谱中的一度关联节点数、CEP中的有限状态机等,这些信息可能会在后续的流数据分析过程中被继续使用,从而需要将这些信息保存下来。另外,在后续的流数据处理过程中,这些信息还会被不断地访问和更新。我们将这些分析所得并保存下来的数据称为流信息状态。

为什么区分这两种状态非常重要?思考这么一个问题,如果我们要计算“用户过去7天交易的总金额”,该如何做?一种显而易见的方法是直接使用各种流计算框架都提供的窗口函数来实现。例如,在Flink中如下:

userTransactions

.keyBy(0)

// 滑动窗口,每秒计算一次7天窗口内的交易金额.timeWindow(Time.days(7), Time.seconds(1))

.sum(1);

图5-1 流数据状态和流信息状态

上面的Flink示例代码使用timeWindow窗口,每秒计算一次7天窗口内的总交易金额。其他流计算平台如Spark Streaming、Storm等也有类似的方法。

聪明的读者一定发现,这似乎有些怪怪的,到底哪里不妥呢?笔者认为至少有以下几点非常不妥。

·每秒才能输出计算结果,而如果我们需要每来一个事件就要计算一次该事件所代表的用户在“过去7天交易的总金额”,则这种做法显然是不可行的。

·窗口为7天,滑动步长为1秒,这两个时间相差的数量级太大了。这意味着需要在“7天除以1秒”这么多个窗口中重复计算!当然,这里设置1秒是因为我们想尽可能地“实时”。如果觉得1秒太“过分”了,则我们也可以将滑动步长设置为30秒、60秒等,但这并不能改变重复计算的本质,且滑动步长越长,离“实时计算”越远。·窗口为7天,我们需要在实时流计算系统中缓存7天的流数据。

我们想要得到的其实只是一个聚合值而已,所以保存7天完整的流数据似乎有些“杀鸡用牛刀”。当然,Flink对诸如sum、max、min之类的窗口聚合计算做了优化,可以不用保存窗口内的全部数据,只需要保留聚合结果即可。但是如果用户需要做一些定制化操作(如自定义Evictor,就会保存窗口内的全量数据了。另外,对于诸如关联这样的操作,肯定要保存窗口内的全部数据。

·如果我们要在一个事件上计算几十个类似于“用户过去7天交易的总金额”这样的特征,则按照timeWindow的实现方法,每个特征可能会有不同的时间窗口和滑动步长,该怎样同步这几十个特征计算的结果呢?

所以在很多情况下,直接使用由流计算框架提供的窗口函数来实现诸如“时间维度聚合特征”的计算问题,我们都会遇到问题。究其根本原因,是因为我们混淆了“对流的管理”和“对数据信息的管理”这两者。因为“窗口”实际上是对“流数据”的分块管理,我们用“窗口”来将“无穷无尽”的流数据分割成一个个的“数据块”,然后在“数据块”上做各种计算。这属于对流数据的“分而治之”处理。我们不能将这种针对“流数据”本身的分治管理模式与我们对数据的业务信息分析窗口耦合起来。

因此,我们需要将“对流的管理”和“对数据信息的管理”这两者分离开。其中,“对流的管理”需要解决诸如窗口、乱序、多流关联等问题,其中也会涉及对数据的临时缓存,它缓存的是流数据本身,因此我们称之为流数据状态;而“对数据信息的管理”则是为了在分析和挖掘数据内含信息时,帮助我们记录和保存业务分析结果,因而称之为流信息状态。

流数据状态

在流数据状态管理中,比较重要的操作是事件窗口、时间乱序和流的关联操作。流数据状态最理想的情况是只保存在内存中,只有在做持久化(checkpoint)时,才写入磁盘。这样做的原因在于,流数据从接收、处理到删除,具有实时、快速和临时的特点,如果每次接收到一个新事件,都要将其持久化到磁盘,势必会引起性能的急剧下降。

但将所有数据全部放在内存终究太过理想。大多数场景下,我们需要分析的窗口内的数据量都超过了内存容量,所以此时流数据状态也可以存放在文件或其他外部存储系统中。这种情况下,每次窗口计算都需要访问内存外的数据,会对性能造成一定的影响。这样的好处是避免了内存对数据量的限制。

下面我们分析需要使用流数据状态的3种重要原因。

事件窗口是产生流数据状态的主要原因。在第3章实现的流计算框架和应用中,事件的处理方式是来一个就处理一个,并没有“窗口”的概念。但在实际很多场景中,我们并不需要每来一个事件就处理一个,而是按照一定的间隔和窗口来处理事件。例如,“每30秒计算一次过去5分钟的交易总额”“每满100个事件计算平均交易金额”“统计用户在一次活跃期间点击过的商品数量”等。对于这些以“窗口”为单元来处理事件的方式,我们需要用一个缓冲区(buffer)临时地存储过去一段时间接收到的事件,等触发窗口计算的条件满足时,再触发处理窗口内的事件。当处理完成后,还需要将过期和以后不再使用的数据清除掉。

另外,在实际生产环境中,可能会出现故障恢复、重启等情况,这些“缓冲区”的数据在必要时需要被写入磁盘,并在重新计算或重启时恢复。

解决时间乱序问题是使用流数据状态的另一个重要原因。由于网络传输和并发处理的原因,在流计算系统接收到事件时,非常有可能出现事件已经在时间上乱序的情况。例如,时间戳为1532329665005的事件比时间戳为1532329665001的事件先到达流计算系统。怎样处理这种事件在时间上乱序的问题呢?通常的做法是将收到的事件先保存起来,等过一段时间后乱序的事件到达时,再将其和保存的事件按时间排序,这样就恢复了事件的时间顺序。当然,这个过程存在一个问题,即“等过一段时间”到底是怎样等及等多久?针对这个问题有一个非常优秀的解决方案,即水印(watermark)。

使用水印解决时间乱序问题的原理如下:在流计算数据中,按照一定的规律(如以特定周期)插入“水印”,水印是一个时间戳,当处理单元接收到“水印”时,表示应该处理所有时间戳在该水印之前的事件。我们通常将水印设置为事件的时间戳减去一段时间的值,这样就给先到的时间戳较大的事件一个等待晚到的时间戳较小的事件的机会,而且确保不会没完没了地等待下去。在这个过程中,等待时间的大小就是那个减去的时间段了。当然,这种方案也不能百分百地解决时间乱序问题,实在太晚到达的事件当然只能是“过期不候”了。

因为解决时间乱序问题需要等待晚到的事件,所以不可避免地会对当前事件的处理带来一定时延。

流的关联操作也涉及流数据状态的管理。在关系型数据库中,关联操作是一种非常普遍的行为。现在这个概念也越来越多地被延伸到流计算上来。常见的关联操作有join和union。特别是在实现join操作时,需要先将参与join操作的各个流的相应窗口内的数据缓存在流计算系统内,然后以这些窗口内的数据为基础,做类似于关系型数据库中表与表之间的join计算,得到join计算的结果,之后再将这些结果以流的方式输出。很显然,流的关联操作也需要临时保存部分流数据,故而其也是一种“流数据状态”的运用。当然,除了以上3种“流数据状态”的主要用途外,其他地方也会涉及流数据状态的管理,如排序(sorting)、分组(group by)等。

不管怎样,这些操作都有一个共同的特点,即它们需要缓存的是部分原始的流数据。换言之,这些操作要保存的状态是部分“流数据”本身。这也正是将这类状态取名为“流数据状态”的原因。

流信息状态

流信息状态是为了记录流数据的处理和分析过程中获得的我们感兴趣的信息,这些信息会在后续的流处理过程中被继续使用和更新。

以“实时计算每个交易事件在发生时过去7天交易的总金额”为例,我们可以将每小时的交易金额记录为一条状态,这样,当一个交易事件到来时,我们计算“过去7天的交易总金额”,就是将过去7天每小时的总交易金额读取出来,然后对这些金额记录求总和即可。在上面这个例子中,将每小时的交易金额记录为一条状态,即我们所说的“流信息状态”。

流信息状态的管理通常依赖于数据库完成。这是因为对于从流分析出来的信息,我们可能需要保存较长时间,而且数据量会很大,将这些信息状态放在内存中,势必会占用过多的内存,这是不必要的。

对于保存的流信息状态,我们并不是在每次计算中都会用到,因此会存在冷数据和过期淘汰的问题。所以,将流信息状态,交给专门的数据库管理是非常明智的。毕竟到目前为止,各种数据库的选择十分丰富,而且许多数据库对热数据缓存和TTL机制都有非常好的支持。

相比流数据状态主要由流计算框架原生提供,流信息状态则与业务本身关系更近,并且在实际开发过程中也是主要的处理对象,需要我们做更多的工作。

因此,在接下来的两节中,我们将详细讨论几种非常典型的流信息状态管理方案。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值