第1篇:通过流式数据集成实现数据价值(1)
第2篇:通过流式数据集成实现数据价值(2)
第3篇:通过流式数据集成实现数据价值(3)- 实时持续数据收集
本篇为通过流式数据集成实现数据价值的第4篇——流数据管道。
从实时源收集数据后,会将其添加到数据流中。流包含随时间推移可用的一系列事件,每个事件包含来源端的数据以及标识源端属性的元数据。流可以是无类型的,但更常见的是,流的数据内容可以通过内部(作为元数据的一部分)或外部数据类型的定义来描述。流是无界的、不断变化的,可能是无限的数据集,与传统的有界,静态和有限批次的数据有很大不同。在本章中,我们讨论流数据管道。
流和批之间的差异
以下是数据流的主要用途:
- 促进异步处理
- 启用数据的并行处理
- 支持时间序列分析
- 在数据管道中的组件之间移动数据
- 在集群处理平台的节点之间移动数据
- 跨网络边界移动数据,包括数据中心到数据中心,数据中心到云
- 可靠和有保证的方式,可以处理故障并支持恢复
流促进了数据的异步处理。数据流、流处理和数据交付不需要与数据摄入紧密耦合,它们可以在一定程度上独立工作。但是,如果数据消费速率与摄入速率不匹配,则可能会导致积压,需要通过反压力或持久微信来处理积压,本章稍后将详细讨论这一问题。
流还支持并行处理数据。当在群集处理平台中的多个节点之间存在逻辑数据流时,可以通过流分区机制确定将在其上处理特定事件的节点。该机制利用数据的键或其他功能,以确定性和可重复的方式将事件一致地映射到节点。
传递到流的数据通常是跨时间的。这意味着数据可能有多个时间戳,可用于时间序列分析。时间戳可能出现在原始数据或元数据中,或者可以在收集或处理时注入到流事件中。这些时间戳支持事件排序、基于时间的聚合和流处理的其他关键特性。
让我们开始通过它们最重要的功能来检查流:以可伸缩的方式在线程,进程,服务器和数据中心之间移动数据,并且延迟非常短。
4.1 移动数据
了解流最重要的是它是逻辑实体。这意味着一个命名的流可以包含在不同位置运行的多个物理组件。它具有逻辑定义和物理位置。流是对多种实现的抽象,使它能够在许多不同的网络拓扑中有效地移动数据。
为了了解各种可能性,我们以一个简单的示例为例,它是一个源读取器,它实时收集数据并将其写入流中。目标写入器从该流中读取数据,并将数据实时传递到目的地。
下图说明了此简单数据流中涉及的组件。
下面提供了每个组件的描述:
- 源:实时数据的来源。例如,数据库、文件、消息等等
- 读取器:从源收集实时数据并写入流
- 流:数据元素从一个组件、线程或节点到下一个组件、线程或节点的连续移动
- 网络:描绘不同的网络位置。例如,on-premises和cloud
- 节点:运行进程的机器
- 进程:操作系统进程
- 线程:一个进程中独立并发的编程流
- 组件:在线程中运行的可以与流交互的项
- 写入器:从流中接收实时数据并写入目标
- 目标:实时数据的目标。例如,数据库、Hadoop等等
在所有情况下,读取器将写入一个命名流,而写入器将从相同的命名流接收数据。这个流的最简单的工作方式是在单个线程、单个进程和单个节点上运行所有内容。
在这种情况下,流实现可以是一个简单的方法(或函数)调用,因为读取器直接将数据传递给写入器。通过流进行的数据传输是同步的,不需要序列化数据,因为读取器和写入器在相同的内存空间中操作。但是,组件的直接耦合意味着写程序必须在它们可用时立即消费来自读取器的事件,但写入和读取不能同时并发。写入方面的任何迟缓都会减慢阅读速度,有可能导致延迟。
单线程命名流
为了实现并发,需要一个多线程模型,其中读取器和写入器独立且并行运行。
在这种情况下,流需要跨越线程,并且最通常实现方式为队列。该队列只能是内存中的,也可以根据需要溢出到磁盘上,以满足处理数据大小的要求。读取器和写入器现在可以以流作为缓冲区以异步和以不同的速度运行,以处理偶尔的写入器慢到队列的限制大小。与单线程模式一样,不需要数据序列化。
在多线程应用程序中,操作系统可能导致线程之间出现瓶颈。即使在多核或多CPU系统中,也无法保证单独的线程将在不同的核上运行。如果读取器线程和写入器线程在同一内核上运行,性能将不会比单线程实现好,甚至会差。
多进程模型可以帮助解决这个问题,它使用处理器关联性将CPU内核分配给特定的进程。
在这种情况下,读取器和写入器在不同的操作系统进程中运行,因此流需要跨越两者的内存空间。这可以通过多种方式来完成,利用共享内存,利用传输控制协议(TCP)或其他套接字连接,或者利用第三方消息传递系统实现流。要在进程之间移动数据,将需要将其序列化为字节,这将产生额外的开销。
这种拓扑的自然扩展是在单独的节点上运行读取器和写入器线程,并且流跨越两个位置。
在单独的节点上运行读取器和写入器线程
这样可以确保处理器的充分利用,但消除了将共享内存用于流实现的可能性。相反,流必须使用TCP通信或使用第三方消息传递系统。与前面的示例一样,数据必须序列化为字节,才能通过节点之间的线路发送。节点之间的TCP延迟高于进程之间的延迟,这会增加总体数据流延迟。对于仅可从特定物理计算机访问源或目标的情况,此拓扑也很有用。例如,这些节点可以在本地到云的拓扑中运行在相同的网络域或跨网络中。
跨接网络可能会对流实现提出其他要求。例如,本地网络可能无法从云访问。可能涉及防火墙或网络路由。内部部署部分通常会连接到云中,以实现数据传输,但反之则不然。
流还可以通过分区来并行处理数据。对于单个读取器或写入器无法处理实时数据生成的情况,可能需要使用多个并行运行的实例。例如,如果我们以每秒100,000次操作的速度生成CDC数据,但一个写入器每秒只能管理50,000个操作,那么将负载分配给两个写入器可能会解决此问题。
然后,我们必须仔细考虑数据的分区方式。毕竟,任意分区可能导致时序问题和数据不一致,因为两个异步运行的写入器可能会导致乱序事件。
在单个节点和进程内,我们可以通过从同一流中运行多个写入器线程来实现并行。
通过从同一流中运行多个写入器线程来实现并行
每个线程将基于分区方案接收一部分数据,并同时将数据传递到目标。建议的最大写程序线程数取决于多个条件,但通常应不大于可用的CPU内核数(减去一个读内核),前提是要适当地分配线程(通常不分配)。流应注意将分区数据适当地并行传递到每个线程。
为了获得更高级别的并行性,可能有必要在多个节点上运行多个写入器实例。
同样,流需要照顾数据的分区:在这种情况下,基于分区而不是单独的线程将其发送到不同的节点。还应该有可能将两种并行机制结合在一起,以使多个线程在多个节点上运行,以充分利用可用的CPU内核。可能的并行度将在很大程度上取决于数据的性质以及对连续一致性的要求。
例如,如果对那些表进行的操作是独立的,则有可能按表对CDC数据进行分区。但是,如果对相关表进行了更改(例如,提交了对多个表进行修改的订单),则可能需要按顺序处理结果事件。这可能需要按客户或位置进行分区,以便所有相关事件在同一分区中进行处理。
这些示例处理了从源读取数据并写入目标的简单情况。应该清楚的是,即使在这个基本用例中,也有许多可能的实现选项可以处理吞吐量,规模和延迟。但是,许多实际用例需要某种程度的流处理,这需要多个流和流水线的概念。
4.2 管道的力量
流数据管道是一种数据流,其中事件通过一个或多个处理步骤转换,这些步骤从“读取器”收集到并由“写入器”传递。我们将在本书的后面部分更详细地讨论这些处理步骤,但是到目前为止,足够全面地理解这些步骤就足够了。通常,它们从流中读取数据,并且可以在将数据传递到辅助流之前对其进行过滤,转换,聚合,丰富和关联数据(通常通过类似于SQL的语言)。
下图展示了一个基本管线,该管线在读取器和写入器之间的单个步骤中执行数据的某些处理(例如,过滤)。
基本管道可一步完成过滤
我们可以将其扩展为多个步骤,每个步骤都输出到中间流,如下图。
使用多个步骤执行流程
上一节中讨论的规则和拓扑也适用于这些管道。上图每个流都可以有多种实现方式,可以实现单线程,多线程,多进程和多节点处理,并可以进行或不进行分区和并行化。诸如持久性流、窗口、事件存储、键/值存储和缓存之类的附加功能的引入为数据管道的物理实现增加了更多的复杂性。
流处理平台需要原子地处理任意复杂的数据管道的部署(即整个管道已部署或什么都不部署),在分区、并行性、资源使用和其他指标的基础上采用明智的默认流实现,同时仍然允许用户指定特定的行为来优化生产环境中的流。
4.3 持久流
如前所述,数据流是无限制的连续事件序列,其中每个事件都包含来自外部或中间数据源的数据和元数据(包括时间戳)字段。传统上,为了在流上连续运行处理查询,流发布者和使用者使用典型的发布/订阅模型,在该模型中,主内存用于绑定一部分流数据。然后检查此绑定部分(单个事件还是多个事件)以进行处理,然后丢弃以免耗尽主内存。这样的流本质上总是短暂的。流中的事件一旦被丢弃,便无法再访问它们。
如前所述,当纯粹以内存方式处理流时,自然会产生一些挑战:
- 订阅者必须在流到达时对其进行处理。因此,消费模型与发布者紧密相关。如果发布者发布事件,但订阅者不可用(例如,由于故障),则该事件无法提供给订阅者。 如果有多个数据流进入流处理系统,则如果从内存中丢弃这些事件,则从外部系统对这些流的后续重播将无法保证先前已确认事件的确切顺序。
- 如果流的使用者接收流很慢,则流的发布者可能会停滞。这对处理吞吐量有影响。
持久流是在处理之前首先可靠且有效地写入磁盘的流,这样可以保留事件的顺序以解决上述挑战。这样一来,外部源就可以首先将传入流的事件序列写入磁盘,并让订阅者独立于发布者使用这些事件。最主要的是,从实现的角度来看,这是透明的。