流数据库——流处理基础英雄之旅总是从召唤开始。无论以何种方式,指引者必须出现,对你说:“看,你在梦乡。醒来吧。去旅行吧。
英雄之旅总是从召唤开始。无论以何种方式,指引者必须出现,对你说:“看,你在梦乡。醒来吧。去旅行吧。你还有一部分意识和存在从未被触及。所以你在这里感到安逸?那么,你在那里还不够。”于是,旅程就此开始。
——约瑟夫·坎贝尔,《生活艺术的沉思:约瑟夫·坎贝尔随想录》
流数据库是一个源自十多年数据处理和服务的概念。流数据库的出现是数据库管理系统、数据处理及数字时代不断变化的需求的演变结果。要理解这一演变,让我们通过历史的关键里程碑来探讨流数据库的发展历程。
20世纪末,互联网的兴起和数字数据的爆炸式增长导致对更具扩展性和灵活性的数据管理解决方案的需求。在这一时期,数据仓库和面向批处理的框架(如Hadoop)应运而生,以应对数据规模带来的挑战。
“大数据”这个术语被用来指代不仅仅是数据的规模,还有存储和处理极其庞大数据的所有解决方案。大数据无法放在单一的计算机或服务器上。你需要将它分割成更小、大小相等的部分,并存储在多台计算机中。Hadoop 和 MapReduce 等系统因此流行起来,因为它们实现了分布式存储和处理。
这引出了使用分布式流处理将大量数据传输到 Hadoop 的想法。Apache Kafka 就是这样一种为处理大数据而设计的消息服务。它不仅提供了一种将数据从一个系统传输到另一个系统的方法,还提供了一种实时访问动态数据的方式。这一发展推动了对实时流处理应用的需求。
新的技术如 Apache Flink 和 Apache Spark 应运而生,并能够满足这些新需求。作为批处理和流处理的分布式框架,它们能够跨多个服务器处理数据并提供分析结果。与 Kafka 相结合,这三者提供了一种支持实时流分析用例的解决方案。我们将在第2章中更详细地讨论流处理器。
在2010年代中期,出现了更简单且更优秀的流处理范式,以提高实时数据处理的规模性。这包括两个新的流处理框架,Apache Kafka Streams (KStreams) 和 Apache Samza。KStreams 和 Samza 是最早实现物化视图的,它们使流处理看起来更像一个数据库。
马丁·克莱普曼 (Martin Kleppmann) 更进一步推动了数据库与流处理的结合。在他2015年的演讲“将数据库翻转过来”中,他描述了一种实现流处理的方法,即将数据库的内部功能外部化为实时流。这种方法导致了更具扩展性、弹性和实时性的流处理系统。
流处理的一个问题是(至今仍然是)它比批处理更难使用。流处理的抽象较少,更多的底层技术显露出来。为了实现流处理,数据工程师现在不得不考虑数据顺序、一致性以确保准确处理、故障容忍、弹性、可扩展性等等。这成为了一个阻碍,阻止了许多数据团队尝试使用流处理。结果,大多数团队仍然选择继续使用数据库进行数据转换,并以批处理的方式运行数据处理,尽管这可能无法满足性能要求。
在本书中,我们希望让那些习惯于使用数据库的人更容易接触到流处理和流处理技术。我们将从克莱普曼所提出的“将数据库翻转过来”开始,讨论如何实现这一转变。
将数据库翻转过来
马丁·克莱普曼(Martin Kleppmann)是一位杰出的软件开发人员,他曾发表了一场发人深省的演讲——《将数据库翻转过来》。在演讲中,他介绍了 Apache Samza,这是一种实现流处理的新方法,将数据库的内部功能外部化为实时流。他的思想领导力引发了范式转变,将物化视图引入了流处理。
“其实这是一种悄然尝试,将我们所熟知的数据库架构翻转过来。”
——马丁·克莱普曼,《将数据库翻转过来》
然而,流处理依然困难,因此,许多数据工程师在一段时间内仍然选择继续使用数据库来转换数据并以批处理的方式运行,即便这意味着无法满足服务水平协议(SLA)要求。
在本书的接下来的章节中,我们将尝试通过将流处理重新引入数据库,使数据工程师更容易接触到流处理技术。但在此之前,我们需要理解为什么克莱普曼决定拆解数据库,以及他在新范式中选择的特定数据库功能如何实现实时数据处理。
外部化数据库功能
克莱普曼识别出了数据库中的两个重要功能:预写日志(WAL)和物化视图。事实证明,这些功能天然具有流处理特性,为实时处理数据提供了一种更好的方式。
预写日志(WAL)
预写日志(WAL)是一种机制,允许数据库确保数据的持久性和一致性。数据库写入数据的旋转磁盘本身并不支持事务。因此,数据库面临着在不支持事务的设备上提供事务性操作的挑战。WAL 使数据库能够在没有事务性磁盘的情况下提供事务性操作。
在数据库中,事务指的是作为单一工作单元执行的一系列一个或多个数据库操作。这些操作可以包括数据插入(INSERT)、数据修改(UPDATE)或数据删除(DELETE)(参见图 1-1)。
预写日志(WAL)充当了一个缓冲区,当有新的更改发生时,该缓冲区可以被覆盖。WAL 将这些更改持久化到磁盘上,如图 1-2 所示。
将事务保存到磁盘时,数据库会按照以下步骤操作:
- 客户端通过发出
BEGIN
语句启动一个事务。 - 数据库向 WAL 写入一条记录,指示事务已经启动。
- 客户端对数据库数据进行更改。
- 客户端通过发出
COMMIT
语句提交事务。 - 数据库向 WAL 写入一条记录,指示事务已经提交。
- 事务所做的更改被写入磁盘。
当事务开始时,数据库会向 WAL 写入一条记录,指示事务已经启动。然后,数据库会进行数据更改。然而,这些更改在事务提交之前不会被写入磁盘。此外,如果数据库崩溃或断电,可以从日志中重新播放这些更改,从而将数据库恢复到一致的状态。
WAL 提供了一种实时捕获数据库事务的机制,外部系统可以订阅它。其中一个用例是数据库灾难恢复。通过读取 WAL,数据可以复制到一个备用数据库中。如果主数据库发生故障,数据库客户端可以切换到备用数据库,该数据库是主数据库的副本(参见图 1-3)。
由于 WAL 是实时接收事务的,因此它们自然具备了流处理的完美语义。客户端可以订阅 WAL 并将其事务转发到流处理平台,供其他系统使用。这些系统还可以构建代表原始主数据库的副本。WAL 结构的语义在像 Kafka 这样的流处理平台的存储实现中得到了模仿。流处理平台将数据库的 WAL 语义外部化,供其他应用程序和系统使用。
关于 WAL 还有其他与流处理相关的概念。在事务提交后,WAL 不会立即被清除,而是遵循一个称为“检查点”的过程,这涉及定期将 WAL 中的事务刷新到主数据文件中。检查点有多个作用,其中之一是确保某些已提交的更改已永久写入数据文件,从而减少在崩溃后恢复过程中需要重新播放的数据量。这有助于加快恢复过程。此外,随着事务的提交,WAL 会随着时间的推移不断增长。检查点通过将部分内容刷新到数据文件中,帮助控制 WAL 的大小,防止 WAL 过大并占用过多磁盘空间。检查点和重放事务也是流处理中的特性,原因非常相似。
我们提到,通常存在于数据库内部的 WAL 结构可以在像 Kafka 这样的流处理平台中外部化,这些平台在系统间复制数据时提供了类似 WAL 的语义。
流处理平台
像 Apache Kafka 这样的流处理平台是分布式的、可扩展的、容错的系统,旨在处理实时数据流。它们为从各种来源获取、存储和处理大量连续数据提供了强大的基础设施。
大多数流处理平台都有一个称为分区的结构。这些结构模仿了数据库中的 WAL。事务被附加到分区中,就像事务被附加到 WAL 中一样。流处理平台可以持有许多分区,以分配流负载并促进水平扩展。分区被分组到称为主题的抽象中,应用程序可以向主题发布或消费事务。
通过将事务发布到流处理平台,你将其发布给了所有可能想要消费它的订阅者。这被称为发布-订阅模型,它对于允许多个不同的消费者使用这些事务至关重要。
对于其他流处理平台,这些结构的名称可能不同。表 1-1 列出了一些替代的流处理平台。Apache Kafka 是当今最流行的流处理平台。在 Apache Kafka 中,这些结构的抽象称为主题(topic),而底层的分区称为分区(partition)。
表 1-1. 替代流处理平台
流处理平台名称 | 描述 | 实现 | 主题名称 | 分区名称 | 是否兼容 Kafka |
---|---|---|---|---|---|
Memphis | Memphis 是传统消息代理的开源下一代替代品。 | GoLang | Station | Stream | 否 |
Apache Pulsar | Apache Pulsar 是一个开源的分布式消息传递和流处理平台,最初在 Yahoo! 开发。 | Java | Topic | Ledger | 是—当前,Pulsar Kafka 包装器支持 Kafka API 提供的大部分操作。 |
Redpanda | Redpanda 是一个开源的流处理平台,旨在提供一种高性能、可扩展且可靠的方式来处理实时数据流。 | C++ | Topic | Partition | 是 |
WarpStream | WarpStream 是一个直接构建在 S3 上的 Kafka 兼容数据流平台。 | GoLang | Topic | Partition | 是 |
Gazette | Gazette 是一个轻量级的开源流处理平台。 | GoLang | Selector | Journal | 否 |
Pravega | Pravega 是一个流处理器,为持续生成的无限数据提供流存储抽象。 | Java | Stream | Stream Segment | 提供 Kafka 适配器 |
注意 在本书中,我们将使用“主题”和“分区”作为流处理平台中保存实时流数据的结构名称。
由于 Kafka 是当今最流行的流处理平台,表 1-1 的最后一列指示流处理平台是否支持 Kafka 客户端。这将允许应用程序用另一个兼容 Kafka 的流处理平台替代 Kafka。
如前所述,分区是流处理平台用于扩展自身的机制。主题拥有的分区越多,它就越能够分配数据负载。这使得更多的消费者实例可以并行处理事务。事务分布到分区的方式是使用分配给事务的键。在图 1-4 中,数据库中的 WAL 被读取并存储到流处理平台的主题中,这是一种比单纯的磁盘更高层次的抽象。
与其将数据存储供他人查询,流处理平台会重构 WAL 并将事务分发到不同的分区中。重构的 WAL 将事务暴露给其他数据系统,以便构建主数据库的副本。
分区是不可变的、仅可追加的日志,流处理平台用它们来捕获和服务事务。许多消费者可以利用偏移量(offset)订阅它们。偏移量对应于事务在分区中的索引或位置。每个主题的消费者都有一个偏移指针,用于跟踪他们在分区中的位置。这使得消费者可以按自己的节奏读取和处理分区中的事务。一个副作用是,流处理平台必须比数据库在 WAL 中保留事务的时间更长时间地保留分区中的事务。Kafka 的默认保留期是 7 天。这为慢速消费者提供了充足的时间来处理主题中的事务。这个属性也是可配置的,可以允许更长的保留时间。
关于图 1-4,你应该把向主题发布事务的方式与写入磁盘的方式区分开来。流处理平台中关于主题的重要事实是,当事务被发布到主题中时,它们仍然被视为流数据。让我们用水的比喻来帮助解释这一点。当你从水龙头取水时,你会认为那是新鲜水。流处理平台中的情况也是如此。当你从主题中消费事务时,它们也被认为是新鲜的。相反,如果你带回家一升水并且一段时间不喝它,那它就被认为是陈旧的。陈旧或停滞的水容易滋生细菌且不干净。这一升陈旧的水更像是批量处理的数据。
另一方面,如果你一个多月不使用水龙头,水龙头中流出的水可能会含有铁锈或碎屑,表明水已经变得陈旧。在这种情况下,水龙头流出的水并不总是新鲜的。流处理平台往往有一种机制可以防止它们处理陈旧的事务。为了避免发布陈旧的事务,主题上会应用保留策略。在用户配置的保留期过后,事务可以被清除。
总结一下,主 OLTP 数据库在将数据存储到旋转磁盘时,通常会写入 WAL。WAL 可以用于将数据复制到辅助 OLTP 数据库,以应对灾难恢复场景。像 Kafka 这样的流处理平台可以通过主题抽象的分区将数据库 WAL 外部化,为其他系统提供原本在 WAL 中的事务。这些系统订阅主题,以便像辅助 OLTP 数据库那样构建原始主 OLTP 数据库中表的副本(参见图 1-5)。因此,流处理平台可以将之前隐藏在 OLTP 数据库系统中的 WAL 公之于众,成为在整个组织中同步数据库系统的工具。
采用类似的方法,我们可以在流处理平台中构建物化视图。
物化视图
在典型的 OLTP 数据库中,物化视图是一种特殊类型的数据库对象,它存储预计算查询或聚合的结果。与常规视图不同,常规视图是虚拟的,会根据底层数据动态生成结果,而物化视图则存储实际数据,使其物理上存储在数据库中。
物化视图的目的是通过预计算并存储结果来提高复杂查询或聚合的性能。当查询引用物化视图时,数据库可以快速从物化视图中检索预计算的数据,而不是从基础数据表中重新计算。这可以显著减少查询执行时间,并提高整体数据库性能,尤其是在面对大型且资源密集型查询时。
在数据库中,物化视图的物化过程通常需要手动刷新,以保持存储结果的更新。示例 1-1 显示了如何在 Postgres 数据库(一种流行的 OLTP 数据库)中刷新物化视图的示例。
示例 1-1. 在 Postgres 中刷新物化视图
REFRESH MATERIALIZED VIEW CONCURRENTLY product_sales;
通过使物化视图能够被更新,存储的数据将始终是最新的,即存储的数据是实时数据。这个特性使物化视图能够自然地融入到流处理框架中。
在上一节中,流处理平台可以保存来自 OLTP WAL 的事务。这些分区模仿了 WAL 结构,因此其他系统可以构建原始 OLTP 数据库中表的副本。同样的方法可以应用于流处理器中以构建表格结构(参见图 1-6)。
我们将在第2章中更多地讨论流处理。第3章则专门探讨物化视图,因为它们对流数据库的重要性非常大。为了更好地解释流数据库,设置一个我们可以贯穿始终的简单用例会有所帮助。在此过程中,我们将识别实现该用例目标所需的每个系统。
用例:点击流分析
让我们先定义一个简单的用例。这个用例将帮助我们更好地理解流数据库,它们如何解决实时用例,以及在设计实时解决方案时带来的优势。
我们的用例将涉及点击流数据。点击流数据是指记录用户在浏览网站、应用程序或数字平台时的操作和互动事件的序列。它提供了用户在线会话期间执行的点击、页面浏览和其他互动的详细记录。
点击流数据可用于多种用途,例如个性化、定向广告、用户细分、欺诈检测和转化率优化。它在网络分析、营销分析、用户体验研究和其他数据驱动的学科中发挥着关键作用。在图1-7中,客户点击了一个产品,生成了一个点击事件,该事件被微服务捕获。然后该点击事件被发送到下游的分析消费系统。
在我们的用例中,一位居住在纽约州伍德斯托克的24岁男性客户使用手机应用程序点击了一件绿色T恤。我们的目标是将点击流数据提供给最终用户,以便他们进行分析并得出有助于数据驱动决策的见解。
假设在这个例子中,我们希望捕获点击事件并将其与现有客户关联。这将有助于分析提供定向营销,并创建更个性化的体验。
我们将进入 OLTP 数据库的 WAL 中的数据称为“事务”。我们将从面向用户的应用程序中捕获的点击称为“事件”。它们最终都会进入像 Kafka 这样的流处理平台,以便我们最终将它们结合在一起。
理解事务和事件
到目前为止,我们将源自数据库的数据称为“事务”。这些是发生了插入、更新和删除操作并被写入 WAL,然后被写入流处理平台中的主题的数据。我们也可以将这些事务称为“变更事件”或简单地称为“事件”。它们可以是插入事件、更新事件和删除事件,就像应用程序上的点击是一个事件一样。
即使它们都是事件,理解它们仍然是不同类型的事件这一点非常重要。一种事件源自数据库表的变化,而另一种事件源自应用程序上的操作。为了区分它们的不同,我们需要简要了解一下领域驱动设计(Domain-Driven Design)。
领域驱动设计
在软件开发中,工程师会使用存在于其业务领域中的对象来建模他们的应用程序。例如,如果你的业务包括客户,那么你将在应用程序中创建一个代表客户的对象。你会为业务领域中的每个对象都这样做。
让我们构建一个描述我们用例中对象的模型。客户和产品是定义该应用程序的领域模型的一部分对象。这些对象被称为实体(entities)。实体存在于 OLTP 数据库中,并经历插入、更新和删除等变更事件。
像点击事件这样的事件捕获了应用程序中实体之间的交互。在我们的例子中,客户点击了一个产品。客户和产品是对象,而点击产品的动作是事件。这在图 1-7 中得到了表示。
我们可以使用句子的结构来描述这种关系。一个句子包含主语、动词和宾语。句子中的主语通常是执行某个动作的实体。动词描述了这个动作。最后,宾语是接受这个动作的实体。在我们的用例中,句子是:
“客户点击了一个产品。”
点击事件通常提供更多信息,因此我们可以通过更多描述来扩展这个句子:
“IP 地址为 111.11.1111 的客户在2023年7月21日东部时间上午11:20点击了产品12345。”
注意,我们不知道客户或产品的名称,也不知道客户的位置或年龄。同样,我们也不知道产品的类型或颜色。在将点击流事件传递给分析之前,我们必须用客户和产品信息来丰富该事件。
你可能会问:“为什么点击事件不能存储在数据库中?”这是一个合理的问题。为什么不通过 WAL 一起读取点击事件和实体?一个主要原因是 OLTP 数据库可能会耗尽空间。如果你考虑一下客户在应用程序中点击商品的频率,将所有这些数据存储在 OLTP 数据库中是不合理的。实体往往变化很慢,可以被删除或更新。相比之下,点击事件是不可变的,只会插入到表中。这种模式也称为“仅追加”(append-only)。点击事件最好通过直接写入流处理平台的微服务来捕获。
另一个需要注意的区别是,动作事件正在被丰富,而实体事件则用于丰富其他数据。在本书中,了解动作事件和实体变更事件之间的区别将非常重要。每种类型在流经数据流管道时都会被不同地处理,直到它们被提供给最终用户。
上下文丰富化
所有形式的分析消费都需要一个事件发生的上下文。如前所述,点击事件只包含与点击相关的信息,但没有客户或产品信息。通常,在点击事件发生时,实体信息并不可用。如果可用的话,在应用程序中收集并丰富点击流数据在经济上不可行且难以扩展,因为数据量太大,而且会产生延迟。
更好的方法是,在实时数据管道的下游执行这一丰富化操作。拥有这些附加信息将有助于做出更明智的决策。例如,如果客户喜欢绿色衬衫并且是20多岁的男性,了解这些信息将有助于做出更智能的决策,并使应用程序更加个性化。
在我们的用例中,点击事件与业务领域中的其他两个实体相关联:客户和所点击的产品。将这些实体的详细信息与点击事件结合起来,将为实时分析创建一个更有吸引力的上下文。吸引力强的分析能够告诉我们更多关于事件的信息,并帮助我们快速应对问题,比如决定是否增加男性绿色衬衫的库存。
我们知道,属于应用程序领域的实体存在于 OLTP 数据库中。我们也知道这些实体的变化会被写入 WAL。但是我们还没有讨论 WAL 中的事件如何进入流处理平台中的主题,从而使其他系统可以消费这些变化事件并构建应用程序中实体的副本。这个副本将使流处理器下游的点击事件能够结合产品和客户信息进行丰富化。创建这个副本的过程称为变更数据捕获(Change Data Capture, CDC)。
变更数据捕获(CDC)
变更数据捕获(CDC)是一种用于数据库和数据集成系统的技术,用于实时捕获和跟踪对数据所做的更改。CDC 的主要目标是识别并捕获对特定表的任何更改事务(插入、更新或删除),并将这些更改事件提供给下游系统或进程使用。
在执行 CDC 时,你可以订阅已经执行的事务流,也可以捕获快照。快照不是像 WAL 中那样的变更事件。在数据库术语中,快照指的是在特定时间点拍摄的数据库(或数据库中的表)的副本,就像用相机拍照一样。从数据库获取的流类似于压缩视频,其中每一帧都不是图片(或快照),而是从一帧到下一帧的像素变化。
注意 只在每一帧中提供变化而不是快照以节省处理时间的视频类型称为增量编码(delta encoding)。增量编码是一种视频压缩技术,它仅存储连续帧之间的差异。这可以显著减少视频文件的大小,同时仍然保持原始视频质量。
CDC 可以通过几种方式实现:
- 监听 WAL 这是我们在本章中讨论的方法,也是捕获数据库中更改的首选方法。它是实时执行的,自然也是流式的。
注意 捕获更改事务的 WAL 方法通常用于像 PostgreSQL 和 MySQL 这样的关系型 OLTP 数据库。我们之所以谈到它,是因为它与流处理平台中保存流的结构非常相似。一些 NoSQL 事务性数据库可能不采用这种方法,但有其他机制来捕获更改。
- 比较快照 这涉及对表进行快照并将其与先前的快照进行比较,以筛选出更改。此操作可能非常耗费资源,特别是当表很大时。此外,这种方法不是真正的实时处理。快照是在间隔时间内拍摄的。在这些间隔之间发生的更改(包括恢复更改)可能会丢失。一些可疑的更改和恢复事件有时可能无法被检测到。
- 比较更新时间戳 这种方法保存上次更改批次的时间戳,并筛选出具有更新时间戳的记录,该时间戳在上次批次之后发生。这种方法需要在表中包含一个更新列,每当记录更改时需要更新该列。这种方法也不是真正的实时处理。
幸运的是,大多数 OLTP 数据库都有某种方式读取它们的 WAL。一些 OLTP 数据库还原生支持将事件提交到流处理平台或其他系统。例如,CockroachDB 提供了一种从自身创建变更提要的方式,支持以下目标:
- Kafka
- Google Cloud Pub/Sub
- Cloud Storage (Amazon S3, Google Cloud Storage, Azure Storage)
- Webhook
这种方式避免了需要客户端订阅 CockroachDB 中的 WAL,而是由 CockroachDB 直接将变更事件推送到 Kafka(参见示例 1-2)。这是首选模式,因为它显著减少了流数据管道的架构复杂性。
示例 1-2. 从 CockroachDB 创建变更提要到 Kafka
CREATE CHANGEFEED FOR TABLE customer, product INTO 'kafka://localhost:9092';
在 OLTP 数据库中本地支持这种功能,基本上使它们更接近于流数据库。我们将在第5章讨论流数据库。
警告 即使你是 Martin Kleppmann 本人,第1至第4章也是第5章之前的重要阅读内容。请不要跳过这些章节,因为它们提供了支持第5章中流数据库介绍的基础信息。
如前所述,这种推送机制减少了架构复杂性。其他不具备此功能的 OLTP 数据库需要额外的组件(称为连接器)来提取数据并将其发布到流处理平台的主题中。
连接器
在流处理中,我们区分出两种主要类型的连接器:
-
源连接器
- 源连接器从数据源系统(例如数据库)读取数据,并将这些数据作为事件流提供。
-
汇连接器
- 汇连接器从事件流中消费数据,并将这些数据写入汇系统(例如数据库、数据仓库、数据湖等)。
图1-8展示了这两种类型的连接器。在大多数情况下,源连接器将静态数据(data at rest)转换为流数据(又称为动态数据,data in motion),而汇连接器则将流数据转换为静态数据。
静态数据
静态数据(data at rest)指的是存储在数据库或文件系统中且不移动的数据。静态数据往往使用批处理或微批处理技术进行处理。从一个源系统批量传输到另一个系统的数据集有一个开始和结束点。处理批量数据的应用程序可以使用作业调度器(如 cron)启动,当数据集处理完毕时,数据处理就结束了。
这与流处理或动态数据(data in motion)正好相反。动态数据意味着数据没有开始也没有结束。处理流数据的应用程序始终在运行,并监听新数据的到来。
现在让我们深入探讨如何实现源连接器和汇连接器。
连接器中间件
像 Kafka Connect、Meroxa、Striim 或 StreamSets 这样的连接器中间件解决方案已经提供了大量开箱即用的连接器,并且通常具有可扩展性以支持更多的源和汇。连接器中间件还提供横向扩展、监控和其他必要功能,尤其是用于生产环境的部署。
Kafka Connect 是 Apache Kafka 项目的一部分。它是一个分布式集群,其中部署了 Kafka 连接器以并行运行。这种类型的部署会增加流处理架构的复杂性。这些集群通常较为庞大,维护起来也十分繁琐。
如果你有大量的数据源和汇,这些集群往往会变得昂贵且消耗大量资源。将这种集成委托给系统自身的嵌入式连接器来解决通常更为合适。
嵌入式连接器
越来越多的数据库提供了嵌入式连接器与流处理平台进行集成。正如我们之前提到的,CockroachDB 就是一个例子。还有更多的数据库已经实现了嵌入式连接器,即它们能够自行从事件流中消费数据。例子包括 Apache Druid、Apache Pinot、ClickHouse、StarRocks、Apache Doris 和 Rockset。
正如我们所说,让数据库解决与流处理平台的集成问题,使它们更接近于成为流数据库。如果使数据库能够将数据推送到流处理平台并从中拉取数据,流处理自然会在数据库中成为一等公民。
自定义构建
连接器也可以通过实现专用微服务来自定义构建。这种方法的优点是灵活性;缺点则显而易见——“重新发明轮子”的需求,尤其是在现有大量功能强大且可扩展的开源连接器(例如,Kafka Connect 中间件的 Debezium 源连接器)的情况下,通常没有必要从头开始实现连接器。
在图 1-9 中,我们展示了实现连接器的三种方式(为了简化起见,插图中仅展示了源连接器;相应的汇连接器实现只需将此插图镜像即可)。
注意
在本书的其余部分,我们将对连接器的实际实现进行抽象。当我们提到“连接器”时,这可以是基于连接器中间件的连接器、内置连接器,或是自定义构建的连接器。
回到我们的示例用例。在这里,我们希望使用产品和客户信息来丰富点击事件。最有可能的是,这些数据将存储在事务性数据库或在线事务处理(OLTP)数据库中。为了将这些数据作为事件流提供,我们需要为该数据库使用一个源连接器。
注意
OLTP 数据库也称为操作型数据库,指的是一种设计用于处理大量事务的数据库类型。OLTP 数据库旨在提供快速的数据访问和更新,这对于需要实时数据处理的应用程序非常重要。
在图1-10中,你可以看到产品和客户信息存储在一个 OLTP 数据库中。两个数据库源连接器从该数据库中读取数据,并将它们写入主题(“产品主题”和“客户主题”)。点击事件则被写入“点击事件主题”。
总结
在本章中,我们通过介绍马丁·克莱普曼及其“将数据库翻转过来”的方法,介绍了一些基础的流处理概念。通过这样做,我们确定了奠定流处理和流处理基础的两个关键特性:数据库的 WAL(预写日志)和物化视图。
我们了解到,流处理平台中的主题实际上是外部化的数据库 WAL,供其他系统订阅。这些系统可以使用 CDC(或其他形式的连接器)从源数据库中构建表的副本,并对实时数据进行处理。
在下一章中,我们将继续讨论点击流用例,并将其带入下一步——流处理平台,在那里将进行数据的丰富化处理。
转载自:https://juejin.cn/post/7402458172172959780