Databricks 通过 Zerobus Ingest 与 Debezium 实现推模式 CDC 写入 Unity Catalog
Push-based CDC into Unity Catalog with Zerobus Ingest and Debezium
Databricks 与 NVIDIA 合作开发的 Debezium Server Zerobus sink 已随 Debezium Server 3.7 发布,可将 PostgreSQL 等数据库的变更事件直接推送写入 Unity Catalog 的 Delta 表,无需中间 Kafka 集群。
运营数据库中的数据驱动着业务决策,而其中大量分析都在湖仓中完成。当数据库位于私有企业网络、客户控制的环境或受限子网中,且其网络策略阻止来自外部服务的入站连接时,将数据迁移到湖仓就变得十分困难。
许多团队需要将 PostgreSQL 的变更数据引入 Databricks,但拥有该数据库的团队也希望自己掌控捕获过程。该团队运行读取预写日志的进程,并决定哪些连接可以离开其网络。另一方面,变更数据落入受治理的 Delta 表,管道和分析师可以在此基础上构建。
Debezium 是一款常见的开源工具,团队可以完全自主部署,从数据库中提取变更数据捕获(CDC)流。它部署在数据库旁边,读取其预写日志并将事件向外推送。Zerobus Ingest 是目标端,一个将记录直接写入 Unity Catalog Delta 表的推送接口。开源的 Debezium Server Zerobus sink 将两者连接起来,无需在捕获端与湖仓之间使用传统的 Kafka 集群。
NVIDIA 和 Databricks 的工程师合作构建了这一 sink,并将其贡献给开源 Debezium 项目。Databricks 构建了带类型的 Zerobus Ingest sink。随后,NVIDIA 贡献了 envelope 模式,该模式会落地完整的 Debezium 变更事件,并附带相关的投递保障机制,例如幂等键以及在源偏移量推进前进行持久化确认。Zerobus Ingest sink 已在 Debezium Server 3.7 版本中发布。
本文介绍 Debezium 和 Zerobus 各自的作用、NVIDIA 贡献给 sink 的原始变更 envelope,以及落地事件如何转化为当前状态表或历史表。如需动手实践演练,包括从本地 PostgreSQL 表到当前状态表的实时运行示例,请参阅 Databricks 社区上的配套操作指南博客。
挑战
将运营数据库的变更可靠地复制到湖仓中,必须同时解决多个问题:
- 可靠地捕获插入、更新和删除。 一行数据被创建、修改并随后删除时,必须为每个事件生成忠实的流。这必须包括删除操作,而基于查询的“复制表”方法通常会遗漏这些操作。
- 保留排序所需的证据。 重建当前状态的下游逻辑必须按源顺序应用变更,才能正确复制源表。
- 避免使源数据库过载。 按计划轮询数据库(
SELECT ... WHERE updated_at > ?)会增加查询负载,且扩展性差。 - 适配私有/客户控制的网络。 当数据库位于锁定的边界之后时,我们知道连接这些系统并摄取数据可能颇具挑战。
- 避免仅仅为了传输而搭建 Kafka。 Kafka 是强大的事件总线,但仅仅为了将 CDC 事件从数据库移动到湖仓而运维一个集群,对于原本不需要它的团队来说是一项巨大的开销。
- 落地数据以便于处理。 变更流必须以湖仓能够处理和查询的形式到达,而不是进入需要额外导出步骤的孤岛。
基于这些需求,我们需要从架构入手,明确哪些数据将从源端流向目标端。
整体目标架构
在本博客中,我们将探讨传统消息总线架构与使用 Zerobus Ingest 的架构之间的对比。
使用消息总线的架构:
在许多企业环境中,从边界内部发起的出站连接远比入站访问更容易获得许可。当你的团队想要运行变更数据流提取代理的部署时,本地推送模型可以让数据向外流向目标端。在传统设置中,这个目标端会是 Kafka 之类的消息总线。运行 Kafka 需要为容量规划投入资源,以预测处理峰值负载所需的 broker 数量(用于物理扩展),并估算消费者跟上生产速率所需的分区数量(用于逻辑扩展)。这需要一个在 Kafka 部署和管理方面有经验的专职团队。
一旦数据进入 Kafka,你就需要完成下一项工作,即把这些事件复制到 lakehouse。这可以通过部署 Spark、Kafka Connector 或其他工具等摄取作业来实现。最终形成从 Debezium 到 lakehouse 的两跳架构。
使用 Zerobus Ingest 的架构:
当你的唯一目标是 lakehouse 时,Zerobus 允许你绕过消息总线等中间跳。这既简化了你的端到端架构,也消除了为数据传输而部署和管理 Kafka 基础设施的需要。当使用 Zerobus 作为 Debezium 的接收端时,它能在高峰时段持续工作,无需调优。
什么是 Debezium?
Debezium 是一个开源的、基于日志的变更数据捕获平台。它不轮询表,而是读取数据库的事务日志,例如 PostgreSQL 的预写日志(WAL)、MySQL 的 binlog 和 MongoDB 的变更流。然后它将每个变更作为结构化事件发出。该项目始于 2015 年,如今支持广泛的连接器生态系统(PostgreSQL、MySQL、SQL Server、Oracle、MongoDB 等)。
每个变更事件都带有一致的信封结构:
before— 变更前的行状态(插入时为 null)after— 变更后的行状态(删除时为 null)\op— 操作类型:c(创建/插入)、u(更新)、d(删除)、r(读取/快照)source— 元数据:数据库、模式、表、WAL 日志序列号(LSN)、事务 ID、快照标志ts_ms— 事件时间戳
Debezium 对主数据库增加的负载极小,因为它采用日志尾部读取而非轮询来以低延迟捕获变更,并保持提交顺序。
独立模式(Debezium Server)与 Kafka Connect 部署
Debezium 有两种运行模式。一种运行在 Kafka Connect 上,另一种是“独立服务器”,可以部署在普通计算资源上。
Kafka Connect 上的 Debezium | Debezium Server(独立) | |
|---|---|---|
运行时 | Kafka Connect 集群内的插件 | 独立的 Quarkus 应用 |
原生目标 | Apache Kafka(然后下沉到其他系统) | 通过接收端适配器直接发送到非 Kafka 目标。(如 Zerobus Ingest) |
何时使用 | Kafka 是你的标准事件总线;你需要 Connect 的分布式 worker、SMT、schema registry | 你不运行 Kafka,或者你想把 CDC 直接推送到专门构建的目标端 |
Debezium Server 是独立运行时,作为源端代理,跟踪事务日志并将变更事件直接发送到 Databricks 的 Zerobus Ingest,无需 Kafka 集群作为传输层。
解决方案:Debezium + Zerobus Ingest
因此我们已确定 Debezium 作为日志跟踪器。现在我们需要将事件送入 Databricks。
Zerobus Ingest 是一种无服务器、基于推送的摄取服务,可将数据直接写入由 Unity Catalog 治理的 Delta 表,无需运行消息代理。生产者连接到 Zerobus 端点并流式传输记录,记录会落入开放格式的 Delta 表。它专为处理高吞吐量、高并发工作负载而构建。通过跳过消息代理这一步骤,我们能够简化架构,并省去为将数据移动到湖仓而扩展和运行摄取管道的维护工作。
与 Debezium 结合后,你的 CDC 管道大致如下:
- Debezium 观察数据库日志,并将每次变更转换为 CDC 事件。
- Zerobus Ingest 将这些事件直接传输到 Databricks 并写入 Delta 表——无需 Kafka 集群作为中间传输层。
- Databricks 在 Delta 表中接收变更流,并可将其下游处理为精选表。
Zerobus Ingest 仅支持追加。变更事件落地后,下一步是通过执行合并来构建 Type-1 维度表,以获取每个给定主键组合的最新版本。
NVIDIA 的贡献:信封模式
Debezium 中的 Zerobus sink 提供两种表契约。Typed 模式(默认)将每个变更事件映射到具有源自身列的表。Envelope 模式由 NVIDIA 贡献,将完整的 Debezium 变更事件落入固定的九列表:
- target_table
- destination
- partition
- operation
- idempotency_key
- key
- value
- source_position
- headers
记录可编码为 JSON(存储在 VARIANT 列中)或 Protobuf。
信封模式保留了下游处理所需的证据。source_position 携带源分区和偏移量,包括 PostgreSQL LSN。idempotency_key 是每个事件的 SHA-256 标识,由其 destination、partition、key、operation 和 source position 派生,因此重新投递的事件会获得相同的键。该键标识事件;它本身不对事件排序,也不对表去重。
如果后续组失败,先前已持久化的组可以再次投递,因此投递为至少一次,重复项在下游通过 idempotency_key 处理。信封模式还按 destination 和 operation 进行过滤,并在摄取前拒绝超大记录,因此其偏移量永远不会被提交。
Databricks 中的数据建模
一旦 CDC 事件落入 Delta,常见的 medallion 风格流程会将原始变更流转化为分析师和应用程序可用的内容:
- Bronze:接收到的原始变更事件。在 envelope 模式下,每个事件一行,携带其操作、变更前后的行镜像、源位置(LSN)以及
idempotency_key。 - Silver:解析后的变更记录,按
idempotency_key去重并按源位置排序。这是源数据的可用表示形式,其中插入/更新/删除被解释以重建当前状态。 - Gold:将运营数据与业务数据结合进行转换以用于报表,例如财务、性能指标等。
有两种塑造变更流的方式值得简要说明:
- Type-1(当前状态):折叠变更日志,使每个键反映其最新值——即源表的当前状态副本。
- Type-2(历史):保留每个版本随时间的变化,因此你可以查询数据在任意时间点的样子。
Databricks Community 上的配套操作指南博客使用本地 PostgreSQL 表端到端地演示了这一过程。它配置 Debezium Server 使用 Zerobus sink,在 envelope 表中检查插入、更新和删除,然后使用 AUTO CDC 构建 Type-1(当前状态)表,按源 LSN 对变更进行排序。
结论
选择合适的 CDC 架构最终取决于你组织的网络限制、运维偏好和现有基础设施。
需求 / 特性 | Debezium + Zerobus(本地推送) | DIY(Debezium + Kafka + Spark) |
|---|---|---|
摄取方向 | 基于推送(源代理向外推送) | 基于推送(源代理向外推送到 Kafka) |
网络要求 | 仅需源网络具备出站访问权限 | 需要具备到 Kafka 集群 / broker 的出站访问权限 |
私有 / 客户网络 | 非常适合锁定或私有网络 | 适合私有网络,但需要管理 Kafka 连接 |
Kafka 基础设施 | 不需要(通过 Zerobus 直接流式传输) | 需要(Kafka 集群作为中间缓冲区) |
连接器管理 | 需要在本地运行 Debezium Server | 运维开销高(管理 Kafka、Connect 和 Spark 摄取作业) |
生态系统兼容性 | 利用完整的 Debezium 连接器生态系统;envelope 模式将原始变更事件落地,用于 AUTO CDC 的 silver 和 gold 表 | 利用完整的 Debezium 和 Kafka Connect 生态系统,配合自定义 Spark 流式写入 Delta(Bronze)和 AUTO CDC 作业 |
通过将 Debezium Server 与 Zerobus Ingest 结合,你可以部署本地推送架构,将变更直接流式写入 Delta 表,无需 Kafka 作为中间消息传输,也不依赖云对象存储中的暂存区,从而简化端到端摄取路径。
这使得 PostgreSQL 捕获仍由拥有该数据库的团队控制,同时数据落地到 Unity Catalog。
开始使用
要自行构建此管道,请按照 Databricks Community 上的分步指南操作。该指南将 PostgreSQL 变更通过 Debezium Server 和 Zerobus Ingest 写入 Unity Catalog 表,然后使用 AUTO CDC 构建当前状态表。有关 Zerobus Ingest 的设置及其其他摄取接口,请参阅 Zerobus Ingest 文档。
常见问题
什么是 Debezium Zerobus Ingest sink?
它是 Debezium Server(独立的 Debezium 运行时)中的一个 sink,可将变更事件直接发送到 Zerobus Ingest。Zerobus Ingest 将它们写入由 Unity Catalog 治理的 Delta 表,因此捕获与湖仓之间无需 Kafka 集群。NVIDIA 与 Databricks 合作完成了这项开源贡献。
什么是 envelope 模式?
Envelope 模式将每个完整的 Debezium 变更事件放入一个固定的九列表,而不是将其映射到源自身的列上。NVIDIA 将其贡献给了该 sink。它保留操作、键、值、源位置以及用于下游处理的幂等键。Typed 模式是默认模式。
该 sink 是否对每个变更恰好投递一次?
不是,投递是至少一次。如果后续一组记录失败,较早的持久组可能会被再次投递。每个事件都带有一个 idempotency_key,因此您可以在下游据此去重。
如何将变更流转为当前状态表?
使用 AUTO CDC 构建一个 Type-1 表,保留每个键的最新版本。按源 LSN 对变更排序,以便它们按源顺序应用。若要保留随时间变化的所有版本,请构建 Type-2 表。
来源:Databricks Blog · databricks.com