流式处理相关概念总结说明
流式处理相关概念总结说明
流式处理是大数据处理领域的重要技术之一,广泛应用于实时数据分析、事件驱动系统等领域。本文将深入探讨流式处理的核心概念,包括事件流的特性、处理范式、拓扑结构、时间概念、状态管理、流表转换、时间窗口以及处理保证等。
前言
人们对流式处理的理解非常混乱。有太多关于流式处理的定义,它们混淆了实现细节、性能需求、数据模型和软件工程很多方面的东西。在关系数据库领域也面临类似的窘境,关系模型的抽象定义总是夹杂了数据库引擎的实现细节和特定局限性。
流式处理还处在发展阶段,一些流行的实现方案的处理方式可能很特别,或者有特定的局限性,但这并不能说明它们的实现细节就是流式处理的固有组成部分。
本文将介绍流式设计与处理的一些相关通用概念,许多流式计算框架比如说 Flink, Spark Streaming, Kafka Streaming 等都会用到这些概念来设计。
什么是流式处理
什么是数据流(也被称为事件流或流数据)?
首先,数据流是无边界数据集的抽象表示。无边界意味着无限和持续增长。无边界数据集之所以是无限的,是因为随着时间的推移,会有新记录不断加入。谷歌和亚马逊等大多数公司采用了这个定义。
这个简单的模型(事件流)几乎可以用来表示任何一种业务活动,比如信用卡交易、股票交易、包裹递送、流经交换机的网络事件、制造商设备传感器发出的事件、发送出去的邮件、游戏场景中的物体移动,等等。这样的例子不胜枚举,因为大部分事情可以被看成一个事件序列。
除了无边界,事件流模型还有其他一些属性:
事件流是有序的
事件的发生都有先后顺序。以金融活动事件为例,先把钱存进账户,然后再把钱花掉与先把钱花掉,然后再把钱存入账户的顺序是完全不一样的。后者会出现透支,前者则不会。不可变的数据记录
事件一旦发生,就不能被改变。一次金融交易被取消,并不是说它消失了,相反,表示前一个交易操作被取消的事件将被添加到事件流中。顾客向商店退货,之前的销售事实并不会消失,退货行为将被视为一个额外的事件。事件流是可重放的
这是事件流非常有价值的一个属性。我们都知道不可重放的流(流经套接字的TCP数据包通常是不可重放的),但对大多数业务应用程序来说,能够重放发生在几个月前(甚至几年前)的原始事件流是非常关键的。可能是为了能够使用新的分析方法纠正过去的错误,或者是为了达到审计的目的。
现在,我们已经知道了什么是事件流,接下来是时候了解“流式处理”的真正含义了。流式处理是指实时地处理一个或多个事件流。流式处理是一种编程范式,就像请求与响应范式和批处理范式一样。下面将对这3种范式进行比较,以便更好地理解如何在软件架构中应用流式处理。
请求与响应
这是延迟最小的一种范式,响应时间在亚毫秒和毫秒之间,通常也比较稳定。这种处理模式一般是阻塞的,即应用程序会向处理系统发出请求,然后等待响应。在数据库领域,这种范式就是联机事务处理(OLTP)。销售点(POS)系统、信用卡处理系统和基于时间的追踪系统通常都使用这种范式。批处理
这种范式具有高延迟和高吞吐量的特点。处理系统按照设定的时间启动处理进程,比如每天凌晨两点开始启动、每小时启动一次,等等。流式处理
这种范式是连续的、非阻塞的。流式处理填补了请求与响应范式和批处理范式之间的空白。
大多数业务流程是持续进行的,只要业务报告保持更新,业务产品线应用程序能够持续响应,处理流程就可以进行下去,不一定需要毫秒级的响应。具有持续性和非阻塞特点的业务流程,比如针对可疑信用卡交易或网络发送告警、根据供应关系实时调整价格、跟踪快递包裹,都可以选择这种范式。
流式处理相关概念
流式处理与其他数据处理非常相似——写一些代码来接收数据,对数据做一些处理(转换、聚合、增强等),然后把生成的结果输出到某个地方。不过,流式处理有一些特有的概念。
拓扑
一个流式处理应用程序包含一个或多个处理拓扑。处理拓扑从一个或多个源数据流开始,经过满是流处理器的图路径,直到结果被写入一个或多个目标数据流。每个流处理器都是一个应用在事件流上的事件转换计算步骤,拓扑经常以DAG 的形式展现。
时间
时间可能是流式处理中最为重要的概念,也是最让人感到困惑的概念。在流式处理中,形成一个通用的时间概念非常重要,因为大部分流式应用程序的操作是基于时间窗口的。
例如,我们可能有一个计算股价5分钟移动平均数的流式应用程序。如果一个生产者因为网络问题离线2小时,并在重新连线后返回2小时的数据,那么我们就需要知道该如何处理这些数据。这些数据大多与过去了很久的5分钟时间窗口有关,而且已经计算并保存结果了。
流式处理系统一般包含以下几种时间:
事件时间
- 事件时间是指事件的发生时间和消息的创建时间,比如指标的生成时间、商店里商品的出售时间、网站用户访问网页的时间,等等。
- 在处理数据流时,事件时间是非常重要的。
日志追加时间
- 日志追加时间是指事件到达并保存到broker的时间,也叫摄取时间。
处理时间
- 处理时间是指消费者应用程序在收到事件之后要对其进行处理的时间。
状态
如果只是单独处理每一个事件,那么流式处理会非常简单。但如果处理流程中包含了多个事件,那么就需要保存一些状态信息:
- 例如,按照类型计算事件的数量、移动平均数、合并两个流以便生成更丰富的信息流,等等。
- 在这些情况下,只看单个事件是不够的,需要跟踪更多的信息,比如这个小时内每种类型的事件有多少个,需要连接、求和、求平均值的所有事件,等等。
如果只是在应用程序中通过变量保存这些状态信息,是不可靠的,一旦程序奔溃那么这些状态信息将丢失,我们需要持久化这些状态,当程序奔溃恢复时可以读取这些信息恢复状态。
流式处理通常涉及以下几种状态:
本地状态或内部状态
- 这种状态只能被单个应用程序实例访问,通常通过内嵌在应用程序中的数据库来维护和管理。
- 本地状态的优点是速度快,缺点是受可用内存的限制。所以,流式处理的很多设计模式会将数据拆分成多个子流,以便使用有限的本地状态来处理它们。
外部状态
- 这种状态通过使用外部数据存储来维护和管理,通常使用NoSQL系统,比如Cassandra。
- 外部状态的优点是几乎没有大小限制,而且可以被应用程序的多个实例甚至是不同的应用程序访问。缺点是使用额外的系统会增加延迟和复杂性,还可能对可用性造成影响,而且外部系统也存在变得不可用的可能性。
- 大部分流式处理应用程序会尽量避免使用外部存储,或者将信息缓存在本地,减少与外部存储发生交互,以此来降低延迟,而如何维护内部状态与外部状态的一致性就成了一个问题。
流和表
表和流
- 表是记录的集合,每条记录都有一个主键标识,并包含了一组由模式定义的属性。表的记录是可变的(可以执行更新和删除操作)。可以通过查询表获知数据在某一时刻的状态。
- 与表不同,流包含了历史变更数据。流是一系列事件,每个事件就是一个变更。
- 表表示的是世界的当前状态,是发生多个变更后的结果。可见,表和流是同一枚硬币的两面——世界总是在发生变化,我们有时候对导致发生变化的事件感兴趣,有时候对世界的当前状态感兴趣。如果一个系统允许通过这两种方式来看待数据,那么它就比只支持一种方式的系统更强大。
表和流之间的转换
- 流处理器可以通过 CDC 捕获表的变更事件,将表数据以流的方式不断接入,比如说 Flink 或 Kafka 通过监听 MySQL 表的 bin log 来获取最新的数据信息。
- 要将流转化成表,需要应用流里所有的变更。这也叫作流的物化。我们需要在内存、内部状态存储或外部数据库中创建一张表,然后从头到尾遍历流里所有的事件,逐个修改状态。在完成这个过程之后,就得到了一张表,它代表了某个时间点的状态。
假设我们有一家鞋店,店里的零售活动可以用一个事件流来表示:
“红色鞋子、蓝色鞋子和绿色鞋子到货。”
“蓝色鞋子卖出。”
“红色鞋子卖出。”
“蓝色鞋子退货。”
“绿色鞋子卖出。”
如果想知道现在仓库里还有哪些库存或到目前为止赚了多少钱,就需要对视图进行物化。从下图可以看出,我们目前还有299双红色鞋子。如果想知道鞋店的繁忙程度,那么可以查看整个事件流,可以看到总共发生了4个顾客事件。我们可能还想知道为什么蓝色鞋子被退货了:
时间窗口
大部分针对流的操作是基于时间窗口的,比如移动平均数、一周内销量最好的产品、99百分位系统负载,等等。两个流的连接操作也是基于时间窗口的——我们会连接发生在相同时间片段内的事件。
例如,在计算移动平均数时,我们想要知道如下信息:
窗口大小
- 你想要计算5分钟内,还是15分钟,抑或一天的平均数?窗口越大就越平滑,但滞后也越多。
- 如果价格涨了,则需要更长的时间才能看出来。Streams提供了一种会话窗口,其大小是通过不活跃的时间段来定义的。开发人员会定义一个会话间隙,所有连续到达且间隙小于这个会话间隙的事件都属于同一个会话。一个大的间隙将开始一个新会话,在这个间隙之后但在下一个间隙之前到达的所有事件都属于这个会话。
窗口移动频率(移动间隔)
- 5分钟平均数可以每分钟或每秒变化一次,或者在有新事件到达时发生变化。时间间隔固定的窗口叫作跳跃窗口(hopping window),移动间隔与窗口大小相等的窗口叫作滚动窗口(tumbling window)。
窗口可更新时间(宽限期)
- 假设我们已经计算出了00:00和00:05之间的5分钟移动平均数,一小时后,又收到了一些事件时间为00:02的事件,那么需要更新00:00~00:05这个窗口的结果吗?或者就这么算了?
- 理想情况下,可以定义一个时间段,在这个时间段内,事件可以被添加到与它们对应的时间片段里。可以规定如果事件延迟不超过4小时,就重新计算并更新结果,否则就忽略它们。
处理保证
无论是否出现故障,都能够一次且仅一次处理每一条记录,这是流式处理应用程序的一个关键需求。如果没有精确一次性保证,那么流式处理就不能被用在要求精确结果的场景中。