此帖最初发布于 WePay 工程博客

在此博客文章系列的上半部分,我们解释了我们在 WePay 设计 Cassandra 流式数据管道的决策过程。在此帖中,我们将把该管道分解为三个部分,并更详细地讨论每个部分

  1. Cassandra 到 Kafka,使用 CDC 代理

  2. Kafka 到 BigQuery,使用 KCBQ

  3. 使用 BigQuery 视图进行转换

使用 CDC 代理将 Cassandra 流式传输到 Kafka

Cassandra CDC 代理是一个 JVM 进程,旨在部署在 Cassandra 集群的每个节点上。该代理由多个相互依赖的处理器组成,它们并发运行并协同工作,将更改事件发布到 Kafka。

快照处理器

此处理器负责引导新表。它查找 CDC 配置以确定快照模式,并在需要时对启用了 CDC 的表执行快照。要对表进行快照,代理将执行完整的表扫描,并将结果集中的每一行转换为单个创建事件,然后按顺序将它们排入内存中的 BlockingQueue

提交日志处理器

此处理器负责监视 CDC 目录中的新提交日志,通过 Cassandra 的 CommitLogReader 解析提交日志文件,将反序列化的变异转换为标准化的更改事件,最后将它们排入与快照处理器相同的队列。

此时,一些读者可能对并发运行快照处理器和提交日志处理器而不是串行运行感到担忧。原因是 Cassandra 使用 客户端时间戳 来确定事件顺序,并以最后写入获胜的方式解决冲突。这个客户端时间戳被刻意存储在每个更改事件中。这就是为什么快照不必在提交日志处理之前执行——顺序稍后在数据仓库中查询数据时确定。

队列处理器

此处理器负责从队列中取出更改事件,将它们转换为 Avro 记录,并通过 Kafka 生产者将它们发送到 Kafka。它还跟踪最近发送的事件的位置,以便在重新启动时能够从上次中断的地方继续。

乍一看,在 CDC 代理中实现内存队列似乎有些夸张。鉴于只有一个线程执行入队操作,另一个线程执行出队操作,性能提升微乎其微。动机在于解耦解析提交日志的工作(应按正确顺序串行执行)与序列化和发布 Kafka 事件的工作(可以由多个线程为不同表并行执行)。尽管目前尚未实现这种并行化,但我们希望将来能够灵活地添加此功能。

有些人可能还会想知道为什么不使用 Kafka Connect,因为它似乎是流式传输的自然选择。如果我们想要具有容错能力的分布式并行处理,这是一个很好的选择。然而,与 Kafka 生产者相比,它的部署、监控和调试更加复杂。为了构建最小可行基础设施,我们当时选择了 Kafka 生产者。

架构处理器

为了支持自动架构演进,此处理器会定期轮询数据库以获取最新的表架构,并在检测到更改时更新内存中的架构缓存。快照处理器和提交日志处理器都从此缓存中查找表架构,并将其作为更改事件的一部分附加到入队之前。然后在出队时,队列处理器会将附加的表架构转换为 Avro 架构以进行记录序列化。

提交日志后处理器

此处理器负责在提交日志被处理后进行清理。默认的提交日志后处理器实现将只执行删除操作。可以为特定用例配置自定义提交日志后处理器,例如将提交日志文件归档到 S3GCS

使用 KCBQ 将 Kafka 流式传输到 BigQuery

一旦事件到达 Kafka,我们就会使用 KCBQ 将事件数据发送到 BigQuery,而无需执行特殊转换,就像我们在 MySQL 流式数据管道 中所做的那样。我们之前写过一篇 博文 详细解释了这个连接器。

使用 BigQuery 视图进行转换

一旦事件进入 BigQuery,这就是繁重的工作所在。我们在原始表之上创建 虚拟视图,以一种镜像 Cassandra 中源表的方式合并数据。请注意,原始表中的每一行都包含有限的数据——只有已修改的列才有状态。这意味着选择每个主键的最新一行将不会为我们提供与源一致的数据。相反,查询必须为每个主键标识每个列的最新单元格。这可以通过对表中每个列的主键进行自连接来实现。尽管连接在 MySQL 中很慢,但 BigQuery 的并行执行引擎和列式存储使得这成为可能。BigQuery 中一个 1TB Cassandra 表上的视图查询大约需要 100 秒。

压缩

BigQuery 视图是虚拟的事实意味着每次查询视图时实际上都会触发原始数据的完整压缩。这意味着成本会随着查询次数的增加而增加,更不用说重复的事件将需要处理的数据量放大了 N 倍(其中 N 是复制因子)。为了节省成本并提高性能,有必要通过具体化视图来定期进行压缩。

未来的开发工作

支持 Cassandra 4.0

在 Cassandra 4.0 中,改进的 CDC 功能允许连接器实时解析写入的事件,而不是在每次提交日志刷新时进行微批量处理。这大大降低了延迟。

性能优化

如前所述,有一个线程负责出队、序列化和发布 Kafka 记录。然而,随着写入吞吐量的增加,如果代理的性能跟不上,就会导致未处理的提交日志堆积,这可能会影响我们生产数据库的健康状况。下一步是利用事件的并行处理来优化性能。

与 Debezium 和 Kafka Connect 整合

我们最初将 Cassandra CDC 代理构建为一个独立项目。现在它已作为 Debezium 连接器开源,我们可以用 Debezium 中的现有类替换我们的一些自定义类。另一个改进是支持所有 Debezium 连接器的通用功能,例如支持多种序列化格式。最后,CDC 代理不是容错的;部署时需要健壮的警报和监控。未来要探索的一个领域是基于 Kafka Connect 构建 CDC 代理作为源连接器,这进一步简化了 Cassandra 连接器与其他 Debezium 连接器的集成,并免费提供了可伸缩性和容错能力。

结束语

Cassandra 作为一个点对点分布式数据库,为 CDC 带来了一些非常有趣的挑战,这些挑战在 MySQL 和 Postgres 等关系型数据库,甚至 MongoDB 等单主控 NoSQL 数据库中并不存在。请注意,在为 Cassandra 部署自己的实时数据管道之前,值得评估其局限性。

除了理解 Cassandra 的内部机制,我们在此过程中还学到了一些关于工程生产力的经验。

最小可行产品理念

通过剥离除基本功能之外的所有功能,我们在有限的资源下,能够在合理的时间内构建、测试和部署一个可行的解决方案。如果我们一开始就打算设计一个包含所有功能的管道,这将花费更长的时间并需要更多的资源。

社区参与

Cassandra 是一个开源项目。与其独自解决问题,不如从一开始我们就与 Cassandra 社区进行互动(例如,通过 聚会 与提交者和用户分享经验,在 邮件列表讨论提案,在 会议上展示概念验证 等);所有这些都在设计和实施阶段为我们提供了宝贵的反馈。

Joy Gao

Joy Gao 是 WePay 的一名软件工程师,专注于变更数据捕获、数据仓库和分布式系统。

   


关于 Debezium

Debezium 是一个开源的分布式平台,可以将现有数据库转变为事件流,使应用程序能够几乎即时地看到并响应数据库中已提交的每个行级更改。Debezium 构建在 Kafka 之上,并提供了 Kafka Connect 兼容的连接器,用于监控特定的数据库管理系统。Debezium 将数据更改的历史记录在 Kafka 日志中,这样您的应用程序可以随时停止和重新启动,并可以轻松地消费在未运行时错过的所有事件,确保所有事件都被正确且完整地处理。Debezium 在 Apache 许可证 2.0 下是 开源 的。

参与进来

我们希望您觉得 Debezium 有趣且有用,并希望尝试一下。在 Twitter @debezium 上关注我们,在 Zulip 上与我们聊天,或加入我们的 邮件列表 与社区交流。所有代码都在 GitHub 上开源,因此请在本地构建代码,帮助我们改进现有连接器并添加更多连接器。如果您发现问题或有改进 Debezium 的想法,请告诉我们或 记录一个问题

版权所有 © Debezium 及其作者。保留所有权利。有关我们的商标详情,请访问我们的 商标政策商标列表。第三方商标属于其各自所有者,在此提及并不表示任何认可或关联。
×