3. Debezium入门

好,咱们正式开始聊 Debezium。说实话,CDC 工具不少,但 Debezium 是我个人用得最顺手的一个。它基于 Kafka,天生就是为流式数据准备的。你想想看,一个数据库的变更日志,能像消息队列一样被消费,这本身就是件很酷的事。

这一节,我会把 Debezium 的架构拆开来讲。包括它的三大组件——Connector、Snapshot、Streaming,还有两种部署模式——Embedded 和 Kafka Connect。最后,我们聊聊 Source Record 和 Offset 这两个核心概念。嗯,这些都是你上手 Debezium 必须搞明白的东西。

Debezium 整体架构

先看一张图,把 Debezium 的骨架搭起来。

Debezium 架构总览 Debezium 架构总览 源数据库 MySQL / PostgreSQL / MongoDB Debezium 核心 Connector + Snapshot + Streaming Change Event 捕获引擎 Apache Kafka Topic / Partition 部署模式:Kafka Connect 部署模式:Embedded 核心概念 Source Record(变更事件) | Offset(偏移量/断点续传) Snapshot 做初始全量,Streaming 做实时增量,两者无缝衔接

这张图把 Debezium 的脉络画清楚了。从左到右:源数据库 → Debezium 核心 → Kafka。下面挂着部署模式和核心概念。咱们一个一个拆。

Connector:连接器的职责

Connector 是 Debezium 的「翻译官」。它负责跟数据库打交道,把数据库的 binlog、WAL 日志或者 oplog 翻译成 Debezium 能理解的事件。

每种数据库都有自己的 Connector。比如:

  • MySQL Connector:读取 binlog,解析 row-based 事件。
  • PostgreSQL Connector:读取 WAL(Write-Ahead Log),支持逻辑复制槽。
  • MongoDB Connector:读取 oplog,或者 Change Streams。
  • SQL Server Connector:读取 CDC 表或变更跟踪。

我的经验:选 Connector 时,一定要确认数据库版本。比如 MySQL 5.7 和 8.0 的 binlog 格式有差异,PostgreSQL 的逻辑复制槽在 9.4 之后才稳定。我踩过 MySQL 5.6 的坑,binlog 格式不支持 ROW 模式,折腾了半天。

Connector 的配置通常长这样:

{
  "name": "mysql-connector-01",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "192.168.1.100",
    "database.port": "3306",
    "database.user": "debezium",
    "database.password": "debezium_pwd",
    "database.server.id": "184054",
    "database.server.name": "myapp-db",
    "database.include.list": "inventory",
    "table.include.list": "inventory.orders,inventory.customers",
    "snapshot.mode": "initial"
  }
}

这里有个关键参数 snapshot.mode,它决定了 Debezium 启动时怎么处理存量数据。咱们接着聊 Snapshot。

Snapshot:存量数据的「快照」

Snapshot 是 Debezium 的「初始化器」。当你第一次启动 Connector 时,它会对数据库做一次全量快照。说白了,就是把表里已有的数据全部读出来,转成 Change Event 发到 Kafka。

为什么要做 Snapshot?因为 CDC 只捕获增量变更。如果你不先做一次全量,那 Kafka 里只有新数据,历史数据就丢了。

Snapshot 有几种模式:

模式 说明 适用场景
initial 启动时执行全量快照,然后切换到 Streaming 默认模式,大多数场景用这个
when_needed 只在需要时执行快照(比如表结构变更后) 需要手动控制快照时机
never 跳过快照,直接进入 Streaming 只关心增量数据,或者已经通过其他方式同步过全量
schema_only 只捕获表结构,不读取数据 只需要 schema 变更事件

注意:Snapshot 期间,Debezium 会锁表吗?这取决于数据库和配置。MySQL 默认使用 snapshot.locking.mode=minimal,只加读锁,不影响写入。但如果你用的是 extended 模式,可能会阻塞写操作。我建议在生产环境先做压测。

Snapshot 完成后,Debezium 会自动记录一个 Offset,标记「全量快照已完成」。然后无缝切换到 Streaming 模式。

Streaming:实时增量捕获

Streaming 是 Debezium 的「实时引擎」。它持续监听数据库的变更日志,把每一条 INSERT、UPDATE、DELETE 都转成 Change Event,实时推送到 Kafka。

Streaming 的核心机制是「轮询 + 解析」:

  • MySQL:轮询 binlog 文件,解析 binlog event。
  • PostgreSQL:通过逻辑复制槽接收 WAL 变更。
  • MongoDB:监听 oplog 或 Change Streams。

Streaming 过程中,Debezium 会不断更新 Offset。这个 Offset 记录了当前读取到的日志位置。比如 MySQL 的 binlog 文件名 + 偏移量,PostgreSQL 的 LSN(Log Sequence Number)。

关键点:Streaming 是「断点续传」的。如果 Debezium 挂了,重启后会从上次记录的 Offset 继续读取,不会丢数据,也不会重复消费。这是 CDC 系统最核心的可靠性保障。

我曾经遇到过一个场景:生产环境的 MySQL 主库 binlog 保留时间只有 24 小时。Debezium 因为网络抖动断连了 30 小时,重启后 Offset 指向的 binlog 已经被清理了。结果就是——数据断层。所以,binlog 保留时间一定要大于你容忍的最大断连时间。我建议至少保留 72 小时。

部署模式:Embedded vs Kafka Connect

Debezium 支持两种部署方式。选哪个,取决于你的架构。

Embedded 模式

Embedded 模式是把 Debezium 作为一个 Java 库嵌入到你的应用里。你写代码启动 Connector,自己管理生命周期。

优点:

  • 轻量,不需要额外部署 Kafka Connect 集群。
  • 适合做「嵌入式 CDC」,比如在微服务里直接捕获变更。

缺点:

  • 你需要自己处理 Offset 的持久化。
  • 扩展性有限,单机部署。

代码示例:

// Embedded 模式启动 Debezium
DebeziumEngine<ChangeEvent<String, String>> engine = DebeziumEngine.create(Json.class)
    .using(props)
    .notifying(record -> {
        System.out.println("Key: " + record.key());
        System.out.println("Value: " + record.value());
    })
    .build();

// 启动引擎
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(engine);

Kafka Connect 模式

这是最主流的部署方式。Debezium 作为 Kafka Connect 的一个 Source Connector 运行。你只需要配置 JSON 文件,通过 REST API 提交给 Kafka Connect 集群。

优点:

  • 分布式,高可用,支持水平扩展。
  • Offset 由 Kafka Connect 自动管理,持久化到 Kafka 内部 topic。
  • 有 REST API,方便运维。

缺点:

  • 需要部署 Kafka Connect 集群,增加运维成本。
  • 依赖 Kafka。

提交 Connector 的 REST API 调用:

curl -X POST http://localhost:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "mysql-connector-01",
    "config": {
      "connector.class": "io.debezium.connector.mysql.MySqlConnector",
      "database.hostname": "192.168.1.100",
      "database.port": "3306",
      "database.user": "debezium",
      "database.password": "debezium_pwd",
      "database.server.id": "184054",
      "database.server.name": "myapp-db",
      "database.include.list": "inventory",
      "table.include.list": "inventory.orders",
      "snapshot.mode": "initial"
    }
  }'

我的建议:生产环境无脑选 Kafka Connect。Embedded 模式适合做原型验证,或者你不想引入 Kafka 的场景。但一旦数据量上来,Kafka Connect 的分布式优势就体现出来了。

核心概念:Source Record 和 Offset

这两个概念是 Debezium 的「灵魂」。搞懂了它们,你就掌握了 Debezium 的数据模型。

Source Record

Source Record 是 Debezium 输出的每一条变更事件。它包含三个部分:

  • Key:主键或唯一键,用于标识被变更的行。
  • Value:变更的详细内容,包括 before、after、source、op 等字段。
  • Headers:元数据,比如数据库名、表名、操作类型。

一个典型的 Source Record(JSON 格式)长这样:

{
  "schema": { ... },
  "payload": {
    "before": {
      "id": 1001,
      "name": "Alice",
      "email": "alice@example.com"
    },
    "after": {
      "id": 1001,
      "name": "Alice",
      "email": "alice_new@example.com"
    },
    "source": {
      "version": "1.9.7.Final",
      "connector": "mysql",
      "name": "myapp-db",
      "ts_ms": 1695000000000,
      "snapshot": "false",
      "db": "inventory",
      "table": "customers",
      "server_id": 184054,
      "file": "mysql-bin.000003",
      "pos": 12345
    },
    "op": "u",
    "ts_ms": 1695000001000
  }
}

字段说明:

  • op:操作类型。c=创建,u=更新,d=删除,r=快照读取。
  • before:变更前的数据(UPDATE 和 DELETE 有)。
  • after:变更后的数据(CREATE 和 UPDATE 有)。
  • source:元数据,记录了这条变更来自哪个数据库、哪个表、binlog 的哪个位置。

重点:source 字段里的 file 和 pos 就是 Offset 的一部分。它们告诉 Debezium:「这条事件是从 binlog 的哪个位置读出来的」。

Offset

Offset 是 Debezium 的「书签」。它记录了当前已经处理到数据库变更日志的哪个位置。下次重启时,Debezium 从 Offset 指向的位置继续读取,保证不丢不重。

Offset 的存储方式取决于部署模式:

部署模式 Offset 存储位置 说明
Kafka Connect Kafka 内部 topic(connect-offsets) 自动管理,高可用
Embedded 文件系统(.offset 文件)或自定义存储 需要自己实现持久化

Offset 的内容示例(MySQL):

{
  "file": "mysql-bin.000003",
  "pos": 12345,
  "row": 1,
  "server_id": 184054,
  "event": 2
}

这个 Offset 表示:「我已经读到 mysql-bin.000003 文件的第 12345 字节位置,第 1 行,第 2 个事件」。

避坑指南:我曾经在 Embedded 模式下忘记配置 Offset 持久化路径,结果每次重启都重新做全量 Snapshot。数据量大的时候,一次 Snapshot 跑几个小时,业务方直接炸毛。所以,Offset 的持久化一定要配好。Kafka Connect 模式下,确保 offset.storage.topic 的副本数大于 1,防止单点故障。

小结

这一节我们把 Debezium 的骨架搭起来了。Connector 负责翻译,Snapshot 做全量初始化,Streaming 做实时增量。部署模式选 Kafka Connect 还是 Embedded,取决于你的架构复杂度。Source Record 和 Offset 是数据模型的核心,搞懂了它们,你就能理解 Debezium 的「断点续传」和「数据一致性」是怎么实现的。