快捷搜索:  汽车  科技

物化视图刷新原理(了解物化视图-)

物化视图刷新原理(了解物化视图-)相比之下,有状态的流处理是复杂的,因为它处理的是 "状态"。" 大多数流处理应用都关注聚合、连接和时间窗口操作。例如,我们可以通过商店的ID来聚合零售交易,以查看每个商店的销售业绩。无状态流处理处理单个事件这篇文章探讨了有状态流处理中的两个基本概念:流和表;以及流如何变成表,形成物化视图。在文章的最后,我们将学习这些物化视图如何被扩展和从失败中恢复。流处理有两大类;无状态和有状态处理。在无状态模式下,你孤立地处理每个事件。最基本的模式是将不必要的事件从流中过滤掉,或对单个事件进行转换。过去的事件对当前事件的处理没有影响。

利用有状态流处理来维护增量更新的物化视图

杜尼特-达努什卡 8分钟阅读

物化视图刷新原理(了解物化视图-)(1)

照片:Etienne Boulanger on Unsplash

在本系列的第一部分,我们了解了物化视图的基本原理,以及它们的缺点。然后,我向你介绍了流处理,作为维护自我更新的物化视图的一种可扩展方式。

这篇文章探讨了有状态流处理中的两个基本概念:流和表;以及流如何变成表,形成物化视图。在文章的最后,我们将学习这些物化视图如何被扩展和从失败中恢复。

有状态的流处理

流处理有两大类;无状态和有状态处理。

在无状态模式下,你孤立地处理每个事件。最基本的模式是将不必要的事件从流中过滤掉,或对单个事件进行转换。过去的事件对当前事件的处理没有影响。

物化视图刷新原理(了解物化视图-)(2)

无状态流处理处理单个事件

相比之下,有状态的流处理是复杂的,因为它处理的是 "状态"。" 大多数流处理应用都关注聚合、连接和时间窗口操作。例如,我们可以通过商店的ID来聚合零售交易,以查看每个商店的销售业绩。

这些聚合需要为流维护一个状态。在前面的情况下,每个存储的运行总量必须在其他地方维护。例如,在一个键/值存储中。下次有交易事件发生时,我们可以查找该事件的商店ID的当前总数,然后进行递增。

物化视图刷新原理(了解物化视图-)(3)

聚合、连接和窗口操作都需要保持一个状态。

本地状态与外部状态

通常情况下,流处理器会将这种状态保存在本地,以便更快地访问。它首先被写入内存,然后最终被刷新到磁盘上的键/值存储,如RocksDB。

但在某些情况下,状态被存储在一个外部地方,如数据库。虽然它引入了额外的延迟,但对于简单的工作负载来说效果很好,并为你提供良好的可扩展性。

有状态的处理引入了许多挑战,尤其是在状态管理方面。你必须在状态的扩展和容错方面花费大量的心思。我们将在接下来的章节中详细讨论这些问题。

物化视图刷新原理(了解物化视图-)(4)

用有状态流处理维护物化视图

传统的数据库支持的物化视图有一个主要问题,那就是它不能增量地更新视图的内容。整个视图必须不时地被重建,这是很昂贵的。

但是,一个有状态的流处理器可以解决这个问题,它将事件流具体化为一个持久的视图,然后在新的数据进来时更新它。流处理器负责视图的维护,这是自动和增量的。一旦有新的事件到来,视图就会被更新,并以尽可能小的方式基于增量进行调整,而不是从头开始重新计算。因此,避免了视图的完全重建。

理解这一点需要你首先熟悉一些概念和行话。让我们在接下来的章节中慢慢解读这些概念。

流和表

在流处理中,有两个基本概念需要我们去理解--流和表。

一个是一个不可改变的、只附加的事件序列,代表了变化的历史。一个包含世界的当前状态,它是许多变化的结果。

在我们上面的零售商店的例子中,一系列的商店交易代表一个流,而商店销售的汇总代表一个表。管理层感兴趣的是当前的销售报告,而不是单个销售。

物化视图刷新原理(了解物化视图-)(5)

有时,我们想要的是当前的状态而不是状态的变化

将一个数据流物化为一个表

为了将一个流转换为一个表,我们需要应用流中包含的所有变化。这也被称为流的物化

为了具体化一个流,我们从头到尾看一遍流中的所有事件,一边看一边改变状态。当我们完成后,我们有一个代表特定时间的状态的表,我们可以使用。这个表可以在内存中,在本地状态中,或者在一个外部数据库中。

实践中的物化视图

为了更好地理解这一点,让我向你展示如何用kafka原生流处理框架ksqlDB构建我们的零售实例。

一个典型的零售交易有以下格式。

{ "order_id":123456767 "customer_id":1232。 "store_id":2123。 "created_at": "2021-09-23" }

然后我们可以定义一个流来代表一系列的交易。

CREATE STREAM transactions ( order_id INT KEY customer_id INT store_id INT total DOUBLE created_at VARCHAR ) WITH ( Kafka_topic = 'trasactions' partitions = 2 value_format = 'json' );

你可能想检查一下当前每个商店的总销售额明细。你可以通过物化一个流的视图来做到这一点。

CREATE TABLE sales_by_store AS SELECT store_id SUM(total) as total FROM TRANSACTIONS GROUP BY store_id EMIT CHANGES;

当你在ksqlDB上运行这个语句时会发生什么?

服务器创建了一个新的持久性查询,永远运行,在数据到达时进行处理。当每条记录从事务流中读取时,持久化查询会做两件事。

  • 递增更新物化视图以整合进入的行。
  • 更新日志主题发出一排信息。

你可以把changelog主题看作是对物化视图的所有更新的审计跟踪。这在我们讨论容错问题时将会很有用。所以我们现在先跳过这一点。

查询物化视图

ksqlDB为客户程序提供了两种将物化视图数据引入应用程序的方式。首先,你可以使用拉动查询,在某个时间点检索结果。

如果你运行下面的查询,当它执行时,结果将是物化视图中的任何内容。

SELECT * FROM sales_by_store WHERE store_id=2000;

相反,我们有推送查询,当查询结果发生变化时,流向你的应用程序。例如,下面的查询连续地将变化日志中的每一行以store_id=2000的方式进行流转。

SELECT * FROM sales_by_store WHERE store_id=2000 EMIT CHANGES;物化视图是如何被持久化的?

在幕后,ksqlDB使用RocksDB来存储物化视图的内容。RocksDB是一个嵌入式的键/值存储,它在每个ksqlDB服务器的进程中运行--你不需要启动、管理或与它交互。

美中不足的是,RocksDB抽象出了在磁盘上以高性能存储和索引关联数据结构的复杂性。作为一个开发者,这让你专注于流处理逻辑,而不是与状态管理作斗争。

物化视图刷新原理(了解物化视图-)(6)

Apache Flink如何维护本地状态

缩小本地状态的规模

现在你已经看到了像ksqlDB这样的流处理器是如何将一个流具体化到本地状态的。

想象一下,我们的流处理器正在从一个有四个分区的Kafka主题中读取事件。

物化视图刷新原理(了解物化视图-)(7)

流处理器通常先将传入的数据写到内存中。然后定期将其刷出到磁盘(本地状态)。如果我们只有一个流处理器的实例,很快它就会耗尽分配的内存和磁盘空间。

为了扩大处理规模,我们可以部署多个流处理器的实例。你可以通过添加更多的流处理器来提高处理的吞吐量。

物化视图刷新原理(了解物化视图-)(8)

但是,我们如何确保一个事件只被一个流处理器所处理?这就是Kafka的消费者组协议的作用。

不同的流处理框架,如Apache Flink、ksqlDB和Kafka Streams都在下面实现了Kafka消费者协议。分区分布在流处理器中,在一个消费者组中只有一个处理器消耗一个分区。

当有多个流处理器时,本地状态被分散到每个处理器实例中。每个处理器只拥有整个事件流的一个子集,这通常由事件的分区键驱动。

这在内存和磁盘空间方面提供了无限的可扩展性。

物化视图刷新原理(了解物化视图-)(9)

容错--从故障中恢复。

我们刚刚解决了扩展的问题。接下来我们来谈谈容错问题。

在我们的零售例子中,如果一个流处理器在处理流的时候发生故障,会发生什么?我们能恢复崩溃的实例的物化本地状态吗?

是的,由于Kafka的变化日志架构,这是有可能的。

早些时候,我们学习了如何将一个流变成一个表。同时,也可以把一个表变成一个流。要做到这一点,我们需要捕获修改表的变化。把所有这些插入、更新和删除的事件,存储在一个流中。这就是我们在ksqlDB例子中看到的changelog流。

在我们的例子中,我们有四个处理器将四个表物化。这些表在Kafka中创建了四个变化日志流。让我们假设流处理器D发生故障。那么Kafka将触发一个分区的重新平衡,这样分区P4就可能被分配给C

物化视图刷新原理(了解物化视图-)(10)

最初,C并没有D所维护的本地表。但是,C可以通过重放D的变化日志流来重建它。

物化视图刷新原理(了解物化视图-)(11)

C通过重放D的变化日志流赶上D的最后已知良好状态。

这种类型的故障恢复已经内置于今天的许多流处理器中,将你从解决分布式系统中最复杂的问题中解放出来。

经验之谈
  • 有状态的流处理器可以将事件流具体化为一个代表当前状态的表格。
  • 这些表经常被存储在流处理器的本地。像RocksDB这样的键/值存储经常被用于此。
  • 状态被分散在多个流处理器实例中,以实现高可扩展性和容错。

下一部分探讨了利用变化数据捕获(CDC)来连接一个流和一个表。

猜您喜欢: