4. Debezium 架构与核心概念:Connector、Snapshot、CDC、Offset、Schema History
好,咱们进入正题。Debezium 这东西,说白了就是一个 CDC(Change Data Capture)平台。它盯着数据库的 Binlog,一有变化就往外推。我刚开始接触它的时候,觉得概念挺多,有点绕。但用熟了就会发现,核心就五个东西:Connector、Snapshot、CDC、Offset、Schema History。
这五个概念,你搞懂了,Debezium 就算入门了。咱们一个一个拆开聊。
4.1 Connector:连接器的角色
Connector 是 Debezium 的“眼睛”。它负责跟数据库建立连接,读取 Binlog。每个数据库实例,或者每张表,都可以配一个 Connector。
我个人习惯,一个 Connector 只监控一个数据库实例。别图省事,把多个实例塞到一个 Connector 里。出了问题,排查起来很痛苦。
Connector 的核心职责:
- 连接数据库,拉取 Binlog 事件
- 将 Binlog 事件转换成 Debezium 内部的消息结构
- 管理 Offset,记录消费位置
- 管理 Schema History,维护表结构变更
举个例子,MySQL Connector 的配置大概长这样:
{
"name": "inventory-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "192.168.1.100",
"database.port": "3306",
"database.user": "debezium",
"database.password": "dbz_pass",
"database.server.id": "223344",
"database.server.name": "myapp",
"database.include.list": "inventory",
"table.include.list": "inventory.products,inventory.orders",
"database.history.kafka.bootstrap.servers": "kafka:9092",
"database.history.kafka.topic": "schema-changes.inventory"
}
}
这里有个坑,我提醒一下。database.server.id 这个值,在 MySQL 集群里必须是唯一的。不能跟其他从库或者主库的 server_id 重复。我曾经见过有人直接复制配置,忘了改这个值,结果 Connector 启动后,MySQL 主库直接报错,说检测到重复的 server_id。嗯,排查了半天。
4.2 Snapshot:初始快照机制
Snapshot,就是“快照”。当 Connector 第一次启动时,它需要知道数据库的当前状态。这时候,Debezium 会执行一次 Snapshot,把表里的现有数据全部读出来,发到 Kafka。
为什么要做 Snapshot?因为 Binlog 只记录变更,不记录历史全量数据。如果你从头开始消费,Binlog 可能早就被清理了。所以,Debezium 先做一次全量同步,然后再切到增量 CDC。
Snapshot 的两种模式:
- initial(默认): 启动时执行一次全量快照,然后切换到 CDC。适合新部署的场景。
- when_needed: 只在需要时执行快照。比如表结构变了,或者 Offset 丢了。
- never: 不执行快照,直接开始 CDC。前提是你确定 Binlog 里还有你需要的数据。
- initial_only: 只做快照,不做 CDC。适合一次性数据迁移。
我记得有一次,线上一个表有 5000 万行数据。默认的 Snapshot 是单线程的,跑了 3 个小时还没完。后来我改成 snapshot.mode: "initial" 配合 snapshot.fetch.size: 10000,分批读取,速度才上来。嗯,这里要注意,Snapshot 期间,数据库的 IO 压力会比较大,最好在业务低峰期做。
4.3 CDC:增量变更捕获
CDC 是 Debezium 的核心价值。Snapshot 做完之后,Connector 就进入 CDC 模式。它持续监听 Binlog,把 INSERT、UPDATE、DELETE 事件转换成消息,推送到 Kafka。
每条 CDC 消息,都包含三个部分:
- Key: 主键值,用于标识是哪一行数据变了。
- Value: 变更前后的数据,以及操作类型(c = create, u = update, d = delete)。
- Headers: 元数据,比如数据库名、表名、操作时间戳。
举个例子,一条 UPDATE 消息的 Value 结构:
{
"schema": { ... },
"payload": {
"before": {
"id": 101,
"name": "旧名称",
"price": 29.99
},
"after": {
"id": 101,
"name": "新名称",
"price": 39.99
},
"source": {
"version": "1.9.7.Final",
"connector": "mysql",
"name": "myapp",
"ts_ms": 1690000000000,
"snapshot": "false",
"db": "inventory",
"table": "products",
"server_id": 223344,
"gtid": null,
"file": "mysql-bin.000003",
"pos": 12345,
"row": 0,
"thread": 7
},
"op": "u",
"ts_ms": 1690000001000,
"transaction": null
}
}
你想想看,有了这个结构,下游系统就能精确知道:哪张表、哪一行、什么时候、发生了什么变化。这就是 CDC 的魅力。
4.4 Offset:消费位置管理
Offset 是 Debezium 的“书签”。它记录了当前消费到了 Binlog 的哪个位置。如果 Connector 重启了,它会根据 Offset 从断点处继续消费,不会丢数据,也不会重复消费。
Offset 存储在 Kafka 的 __consumer_offsets 主题里,或者你也可以配置成存到文件里。我个人建议用 Kafka 存储,因为文件存储容易丢失,而且不好管理。
Offset 丢失的后果:
如果 Offset 丢了,Debezium 会重新执行 Snapshot。对于大表来说,这可能是灾难性的。我曾经遇到过,Kafka 集群磁盘满了,导致 Offset 主题被清理。结果 Connector 重启后,直接重新快照,把数据库压垮了。所以,一定要监控 Offset 主题的保留策略,别让它被自动清理掉。
Offset 的结构大概长这样:
{
"sourcePartition": {
"server": "myapp"
},
"sourceOffset": {
"file": "mysql-bin.000003",
"pos": 12345,
"gtid": null,
"row": 0,
"snapshot": false,
"last_snapshot_record": false
}
}
这里 file 和 pos 就是 Binlog 的文件名和位置。每次消费完一条消息,Debezium 都会更新这个 Offset。嗯,这里要注意,Offset 的提交是异步的,所以极端情况下可能会有少量重复消费。但至少不会丢数据。
4.5 Schema History:表结构历史
Schema History 是 Debezium 的“记忆”。它记录了表结构的历史变更。为什么需要这个?因为 Binlog 事件里只包含数据,不包含表结构。如果表结构变了,比如新增了一列,Debezium 需要知道新列的定义,才能正确解析后续的 Binlog 事件。
Schema History 存储在 Kafka 的一个独立主题里。每次表结构发生变化,Debezium 都会把 DDL 语句记录到这个主题。Connector 启动时,会从这个主题里读取历史 DDL,重建表结构。
Schema History 的配置:
database.history.kafka.bootstrap.servers: "kafka:9092"
database.history.kafka.topic: "schema-changes.inventory"
这个主题的保留策略建议设置为 compact,只保留最新的 DDL 记录。否则,时间长了,主题会变得很大。
我遇到过一个问题:有一次,DBA 直接在主库上执行了 ALTER TABLE,但是 Debezium 的 Schema History 主题因为网络问题,没有成功写入。结果 Connector 重启后,无法解析新的 Binlog 事件,直接报错。后来我加了一个监控,检查 Schema History 主题的写入延迟。一旦延迟超过 5 秒,就报警。嗯,这个坑,你们可以提前规避。
4.6 核心概念关系图
下面这张图,展示了 Connector、Snapshot、CDC、Offset、Schema History 之间的关系。我画得比较直观,你一看就懂。
从图里可以看出来,Connector 是核心调度器。它先触发 Snapshot,把全量数据推到 Kafka。Snapshot 完成后,自动切换到 CDC,持续监听 Binlog。CDC 每消费一条消息,都会更新 Offset,记录消费位置。同时,如果表结构变了,CDC 会把 DDL 记录到 Schema History 主题。这样,即使 Connector 重启,也能从 Offset 恢复位置,从 Schema History 恢复表结构。
4.7 核心概念对比表
最后,我用一张表总结一下这五个概念的区别。方便你以后查阅。
| 概念 | 作用 | 触发时机 | 存储位置 | 丢失后果 |
|---|---|---|---|---|
| Connector | 连接数据库,管理整个流程 | 手动启动 / 自动重启 | Kafka Connect 集群 | 无法捕获变更 |
| Snapshot | 全量数据同步 | 首次启动 / Offset 丢失 | Kafka 主题 | 数据不一致 |
| CDC | 增量变更捕获 | Snapshot 完成后持续运行 | Kafka 主题 | 无法捕获增量变更 |
| Offset | 记录消费位置 | 每次消费后更新 | Kafka / 文件 | 重新 Snapshot |
| Schema History | 记录表结构变更 | DDL 执行时 | Kafka 主题 | 无法解析新结构 |
嗯,这五个概念,你记住了,Debezium 的架构就清晰了。后面咱们会深入每个环节的配置和调优。到时候,你再回头看这张表,会有更深的理解。