Debezium 深度解析:核心架构、连接器配置与快照机制

Debezium 这个项目,我第一次接触是在 2018 年。当时客户需要一个 MySQL 到 Elasticsearch 的实时同步方案,要求延迟在秒级以内。我试了几种方案,最后发现 Debezium 对 binlog 的解析能力确实扎实。这么多年用下来,它已经成了我 CDC 架构里的标配组件。

说白了,Debezium 就是一个基于 Kafka Connect 的分布式 CDC 平台。它把数据库的变更事件转换成流式数据,然后推给下游。你想想看,传统的数据同步要么靠定时批处理,要么靠应用双写,前者延迟高,后者侵入性强。Debezium 的做法是直接读数据库的事务日志——MySQL 的 binlog、PostgreSQL 的 WAL、MongoDB 的 oplog——这样既不影响业务库,又能做到毫秒级延迟。

核心架构:四个关键组件

Debezium 的架构其实不复杂,但有几个关键角色你得搞清楚。

  • Connector:连接器的核心逻辑。负责连接源数据库、读取变更日志、生成事件。每种数据库都有自己的 Connector 实现。
  • Task:Connector 的实际执行单元。一个 Connector 可以拆成多个 Task 并行工作,比如按表拆分。
  • Offset:偏移量管理。记录当前读到日志的哪个位置,保证重启后能断点续传。这个信息默认存在 Kafka 的 __consumer_offsets 主题里。
  • Schema Registry:可选组件。管理事件的结构定义,保证上下游的 schema 一致。我个人习惯在生产环境必开这个。

核心要点:Debezium 的架构设计遵循了 Kafka Connect 的 Worker-Task 模型。Connector 负责定义「读什么」,Task 负责「怎么读」,Offset 负责「读到哪了」。这种分离让扩展变得非常容易。

下面这张图是我自己整理的 Debezium 核心架构流程,你可以看看事件从数据库到 Kafka 的完整路径。

Debezium 核心架构流程图 源数据库 MySQL / PG / Mongo 事务日志 Debezium Connector 解析日志 → 生成事件 Task 执行单元 Offset 偏移量 Kafka Broker 变更事件主题 说明: Connector 定义「读什么」,Task 负责「怎么读」,Offset 记录「读到哪了」。 三者协作实现断点续传和并行读取。

连接器配置:从入门到实战

配置 Debezium 连接器,最核心的就是那个 JSON 配置文件。我见过不少新手在这上面栽跟头,其实关键参数就那么几个。

{
  "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": "223344",
    "database.server.name": "myapp-db",
    "database.include.list": "mydb",
    "table.include.list": "mydb.orders,mydb.users",
    "database.history.kafka.bootstrap.servers": "kafka1:9092,kafka2:9092",
    "database.history.kafka.topic": "schema-changes.mydb",
    "include.schema.changes": "true",
    "snapshot.mode": "initial",
    "tombstones.on.delete": "false"
  }
}

这里我重点说几个容易踩坑的参数。

参数名 作用 我的建议
database.server.id MySQL 复制拓扑中的唯一 ID 不要跟现有从库冲突,建议用 100001 以上的值
database.server.name Kafka 主题的前缀 用业务名+环境名,比如 order-prod
snapshot.mode 启动时是否做全量快照 首次部署用 initial,已有数据用 schema_only
tombstones.on.delete 删除事件是否发墓碑消息 下游用 Kafka Streams 建议 true,否则 false

我曾经踩过的坑:有一次生产环境配置了 database.include.list 但忘了配 table.include.list,结果 Debezium 把整个实例的所有表都同步了,Kafka 主题暴涨。后来我养成了习惯——永远显式指定表级别过滤,哪怕只差一张表也要写清楚。

快照机制:initial vs schema_only

Debezium 启动时,需要先知道数据库的当前状态。这个过程叫快照(Snapshot)。

initial 模式:先做全量快照,再切到增量读取。适合首次部署的场景。它会先锁表(取决于数据库和配置),读取所有数据,记录当前 binlog 位置,然后释放锁,开始流式读取。

schema_only 模式:只读取表结构,不做数据快照。适合已经有数据同步通道的场景。比如你已经在用其他工具做了全量迁移,Debezium 只需要接增量。

嗯,这里要注意:initial 模式在数据量大时可能很慢。我遇到过一张 5 亿行的订单表,initial 快照跑了 6 个小时。后来我改用 schema_only + 外部全量导入的方式,把时间压缩到了 20 分钟。

增量快照:解决大表问题的利器

Debezium 1.6 版本引入的增量快照(Incremental Snapshot),算是解决了一个老大难问题。传统快照是单线程、全量扫描,大表场景下性能堪忧。增量快照的思路是——把全量数据切分成多个 chunk,每个 chunk 作为一个独立的事件流发送。

它的工作原理是这样的:

  1. Debezium 先记录当前 binlog 位置。
  2. 按主键范围分批读取数据,每批生成一个快照事件。
  3. 快照事件和增量事件通过信号表(signal table)协调。
  4. 如果快照过程中有新的变更,Debezium 会做冲突处理,保证数据最终一致。

实战技巧:增量快照的 chunk 大小可以通过 signal 表动态调整。我一般设 1024 行一个 chunk,对于大字段多的表会降到 512。你可以在运行时发信号修改,不用重启连接器。

Debezium Server 部署模式

不是所有团队都用 Kafka。Debezium Server 模式就是为这些场景准备的。它把 Debezium 打包成一个独立服务,输出目标可以是 Pulsar、Kinesis、RabbitMQ、Google Pub/Sub 等。

配置方式很简单,一个 application.properties 文件搞定:

debezium.sink.type=pulsar
debezium.sink.pulsar.client.serviceUrl=pulsar://localhost:6650
debezium.source.connector.class=io.debezium.connector.postgresql.PostgresConnector
debezium.source.database.hostname=localhost
debezium.source.database.port=5432
debezium.source.database.user=debezium
debezium.source.database.password=dbz
debezium.source.database.dbname=mydb
debezium.source.plugin.name=pgoutput
debezium.source.slot.name=debezium_slot

我个人觉得,Debezium Server 适合中小规模场景。如果集群规模超过 10 个连接器,还是建议用 Kafka Connect 模式,管理和监控都更成熟。

Kafka Connect 集成:生产级部署方案

Kafka Connect 是 Debezium 最主流的部署方式。它提供了 REST API、分布式 Worker、自动重平衡、连接器生命周期管理等能力。

部署架构一般是这样的:

  • 2 个以上 Kafka Connect Worker 节点组成集群
  • 每个 Worker 运行多个 Task
  • 通过 REST API 提交/管理连接器
  • 使用 Kafka 内部主题存储配置、偏移量和状态

我习惯的配置参数:

# worker.properties
bootstrap.servers=kafka1:9092,kafka2:9092,kafka3:9092
group.id=debezium-cluster
key.converter=org.apache.kafka.connect.json.JsonConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
key.converter.schemas.enable=true
value.converter.schemas.enable=true
offset.storage.topic=connect-offsets
config.storage.topic=connect-configs
status.storage.topic=connect-status
offset.storage.replication.factor=3
config.storage.replication.factor=3
status.storage.replication.factor=3

关键建议:Kafka Connect 的 offset/config/status 三个内部主题,副本因子一定要设成 3。我曾经因为偷懒设成 1,结果一个 Broker 宕机导致整个集群的偏移量丢失,恢复时花了整整一天。

部署完成后,通过 REST API 提交连接器:

curl -X POST http://connect-worker:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "mysql-orders",
    "config": {
      "connector.class": "io.debezium.connector.mysql.MySqlConnector",
      "tasks.max": "4",
      "database.hostname": "mysql-master",
      ...
    }
  }'

tasks.max 这个参数值得多说两句。它决定了并行度,但不是越大越好。我测试过,对于 MySQL,tasks.max 设为 4-8 效果最好,再高反而因为 binlog 解析的串行瓶颈导致性能下降。

好了,Debezium 的核心内容基本就这些。从架构设计到连接器配置,从快照机制到增量快照,再到两种部署模式的选择,每个环节都有不少细节。实际项目中,我建议你先从 Kafka Connect 模式入手,把基础链路跑通,再根据业务需求调整快照策略和并行度。