如何设计事件流,第 4 部分
扫描二维码
随时随地手机看文章
在之前部分中,我们从宏观角度考虑了我们的数据,并区分了内部数据和外部数据。我们还讨论了模式和数据契约,以及它们如何提供随着时间的推移协商、更改和发展我们的流的方法。最后,我们介绍了事实(状态)和增量事件类型。事实事件最适合通信状态和解耦系统,而 Delta 事件往往更多地用于内部数据,例如在事件溯源和其他紧密耦合的用例中。
规范化表创建规范化流
规范化的表导致规范化的事件流。连接器(例如,CDC)将数据直接从数据库拉入事件流的镜像集。这并不理想,因为它在内部数据库表和外部事件流之间创建了强耦合。
考虑一个简单的电子商务项目及其关联的品牌和税收状态表。
品牌和税收状态表通过外键关系与项目表相关。虽然我们只在表中显示一项,但您可能会有数千(或数百万)项,具体取决于您销售的产品。
通常为每个表设置一个连接器,从表中提取数据,将其组合成事件,并将每个表写入专用的事件流。
公开数据库中的基础表会导致每个表产生相应的事件流。虽然这种方式很容易上手,但它会导致多个问题,这些问题可以概括为耦合 问题或成本 问题。让我们逐一看看。
问题:消费者对内部模型的耦合
按原样公开源Item表会迫使消费者直接耦合它。源系统数据模型的更改将影响下游消费者。
假设我们重构Item表以将P ricing提取到它自己的表中。
重构源表会导致项目流的数据契约损坏。不再向消费者提供他们最初期望的相同商品数据。我们还必须创建一个新的连接器 - 一个新的P米流 - 最后,重构我们的消费者逻辑 以使其再次运行。重命名列、更改默认值和更改列类型是内部数据模型上的紧密耦合引入的其他形式的破坏性更改。
问题:流式连接(通常)昂贵
关系数据库是专门为快速、廉价地解决连接问题而构建的。不幸的是,流连接不是这样的。
考虑两个需要访问Item、其Tax及其Brand信息的服务。如果数据已写入其相应的流,则每个使用者(下图中的右侧)将必须计算相同的连接来对Item、B rand和T ax进行非规范化。
此策略可能会产生高昂的成本,无论是编写应用程序的开发时间还是计算连接的服务器成本。大规模解决流连接可能会导致大量数据洗牌,从而产生处理能力、网络和存储成本。此外,并非所有流处理框架都支持连接,尤其是外键连接。在那些这样做的语言中,例如 Flink、Spark、KSQL 或 Kafka Streams(例如),您会发现自己仅限于编程语言的子集(Java、Scala、Python)。
解决方案:提供非规范化数据是最好的
原则上,应使事件流易于消费者使用。在使用抽象层将数据提供给消费者之前对其进行非规范化,并创建显式的外部模型数据契约 (外部数据)以供消费者耦合。
对内部模型的更改在源系统中保持隔离。消费者获得一个定义明确的数据契约来进行耦合。只要源系统为消费者维护数据契约,对源模型所做的更改就可以不受阻碍地进行。
但是我们在哪里非规范化呢?两种选择:
· 通过专门构建的连接器服务在源系统外部进行重建。
· 在使用事务发件箱模式在源系统中创建事件期间。