3、部署模式与配置:单机、分布式、嵌入式,关键配置项解析

聊到 Debezium 的部署,很多新手第一反应就是「连上数据库,启动一个 Connector,完事」。嗯,真这么简单就好了。我在生产环境里踩过的坑,十有八九都跟部署模式选错、配置项漏了有关。今天咱们就把三种模式掰开揉碎,再顺带把那几个要命的配置项讲清楚。

3.1 三种部署模式,你选哪个?

Debezium 本身不是一个独立运行的进程,它是一组 Kafka Connect 的 Connector。所以它的部署模式,本质上就是 Kafka Connect 的部署模式。我个人习惯按场景来选:

模式 适用场景 容错性 管理复杂度
单机模式(Standalone) 开发测试、小规模数据量 低(进程挂了就没了) 低
分布式模式(Distributed) 生产环境、高可用、大规模 高(自动 rebalance) 中
嵌入式模式(Embedded) 自定义应用、不想依赖 Kafka 集群 由宿主应用决定 高

3.1.1 单机模式

说白了就是一个进程跑一个 Connector。配置简单,启动命令也简单:

bin/connect-standalone.sh config/connect-standalone.properties config/mysql-connector.properties

但你要注意——单机模式没有故障转移。我曾经在演示环境里用单机模式跑了一个月,结果某天服务器重启,offset 全丢了,重新消费了上百万条历史数据。所以,生产环境我从不推荐单机模式,除非你明确知道自己在做什么。

3.1.2 分布式模式

这才是生产环境的主角。多个 Worker 节点组成集群,Connector 和 Task 会在节点间自动分配。如果某个节点挂了,Kafka Connect 会触发 rebalance,把任务挪到其他节点上。

启动方式:

bin/connect-distributed.sh config/connect-distributed.properties

然后通过 REST API 提交 Connector 配置:

curl -X POST -H "Content-Type: application/json" --data '{
  "name": "mysql-connector",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "192.168.1.100",
    "database.port": "3306",
    "database.user": "debezium",
    "database.password": "dbz",
    "database.server.id": "184054",
    "database.server.name": "myapp",
    "database.include.list": "inventory",
    "table.include.list": "inventory.orders",
    "database.history.kafka.bootstrap.servers": "kafka1:9092,kafka2:9092",
    "database.history.kafka.topic": "schema-changes.inventory"
  }
}' http://localhost:8083/connectors

这里有个坑——Connector 配置一旦提交,就由 Kafka Connect 集群管理了。你改配置只能通过 REST API 更新,不能直接改文件。我刚开始用的时候,习惯性地去改 properties 文件,结果发现根本不生效,折腾了半天才反应过来。

3.1.3 嵌入式模式

这个模式比较特殊。它把 Debezium 引擎直接嵌入到你的 Java 应用里,不需要单独跑 Kafka Connect 进程。适合那些不想引入 Kafka 集群、或者需要自定义数据管道的场景。

代码大概长这样:

DebeziumEngine<ChangeEvent<String, String>> engine = DebeziumEngine.create(Json.class)
    .using(props)
    .notifying(record -> {
        System.out.println("收到变更事件: " + record);
    })
    .build();

engine.run();

嗯,这里要注意——嵌入式模式需要你自己管理 offset 的存储。我曾经帮一个团队排查问题,他们嵌入式模式跑得好好的,一重启就重复消费,最后发现是 offset 存在内存里,进程一停就丢了。

3.2 关键配置项解析

配置项很多,但真正决定 Debezium 能不能稳定运行的,就那么几个。我按重要性排了个序:

3.2.1 offset.storage —— 你的断点续传靠它

Debezium 会记录当前消费到 binlog 的哪个位置(offset)。这个 offset 存哪,由 offset.storage 决定。

配置项 说明 推荐值
offset.storage 存储 offset 的 backend org.apache.kafka.connect.storage.KafkaOffsetBackingStore
offset.storage.topic 存储 offset 的 Kafka topic connect-offsets(需提前创建,多分区)
offset.flush.interval.ms 刷新 offset 到存储的间隔 60000(60秒,生产环境建议 5000)

避坑指南:我曾经遇到过一个 case,offset 存在 Kafka 里,但 topic 只有一个分区,导致所有 Connector 的 offset 写入都争抢同一个分区,性能瓶颈非常明显。后来改成 10 个分区,问题解决。所以,offset.storage.topic 的分区数,建议至少跟你的 Connector 数量相当。

3.2.2 database.history —— 你的 schema 演变记录

Debezium 需要知道数据库表结构的历史变化,才能正确解析 binlog 事件。比如,你加了一列,Debezium 得知道新列叫什么、什么类型。

配置项 说明 推荐值
database.history Schema 历史存储实现 io.debezium.relational.history.KafkaDatabaseHistory
database.history.kafka.topic 存储 schema 变化的 topic schema-changes.<server.name>
database.history.kafka.bootstrap.servers Kafka 集群地址 与 offset 使用同一集群

这里有个关键点——database.history 必须持久化。如果历史丢了,Debezium 启动时会发现当前 binlog 位置对应的 schema 跟它记录的不一致,直接报错退出。我见过有人把 history 存在内存里,一重启就全完了。

⚠️ 重要警告:database.history.kafka.topic 不要设置 log.cleanup.policy=delete,建议用 compact 策略。否则历史 schema 被清理后,Debezium 无法解析旧的 binlog 事件。

3.2.3 其他关键配置

  • database.server.name:每个 Connector 的唯一标识,也是 Kafka topic 的前缀。我习惯用 <应用名>-<数据库名> 的格式,比如 order-service-inventory。
  • database.include.list / table.include.list:白名单,只监听指定的库表。千万别不设,否则全库的变更都会发到 Kafka,流量爆炸。我见过一个团队没配这个,结果把 Kafka 集群打挂了。
  • snapshot.mode:首次启动时是否做全量快照。可选值:initial(先快照再增量)、when_needed(需要时自动触发)、never(只增量)。生产环境我一般用 initial,确保数据完整。
  • tombstones.on.delete:删除事件是否发送 tombstone 消息。默认 true,配合 Kafka 的 log compaction 可以清理已删除的数据。

3.3 部署架构图

下面这张图展示了三种部署模式的核心组件关系,我画的时候特意把 offset 和 history 的流向标清楚了:

Debezium 三种部署模式架构对比 单机模式 connect-standalone.sh 单个 Worker 进程 offset 存本地文件 分布式模式 connect-distributed.sh Worker 集群(多节点) offset 存 Kafka topic 嵌入式模式 Java 应用内嵌 宿主应用进程 offset 由开发者管理 关键配置项流向 • offset.storage → 决定 offset 存哪里(文件 / Kafka / 自定义) • database.history → 决定 schema 历史存哪里(Kafka topic 是主流) • database.server.name → 作为 Kafka topic 前缀,全局唯一 • snapshot.mode → 控制首次启动是否做全量快照 • table.include.list → 白名单,避免监听全库导致流量爆炸

3.4 配置最佳实践

最后,分享几个我在实战中沉淀下来的配置习惯:

💡 我的习惯:
  1. offset 和 history 用不同的 Kafka topic。虽然可以共用,但分开更清晰,排查问题也方便。
  2. 给每个 Connector 单独一个 database.server.name,不要复用。否则 topic 会冲突,数据乱掉。
  3. 生产环境用分布式模式,至少 3 个 Worker 节点。单机模式只适合本地调试。
  4. 监控 offset 的 lag。如果 offset 写入太慢,说明 Kafka 集群压力大,需要扩容。
⚠️ 我曾经踩过的坑:
有一次我把 offset.flush.interval.ms 设成了 300000(5分钟),结果 Connector 挂了之后重启,丢了 4 分 50 秒的变更事件。后来改成 5000(5秒),虽然写入频繁了一点,但至少不会丢太多数据。这个值要根据你的业务容忍度来调。

嗯,部署模式和配置项就聊到这。记住一句话:配置不是写出来就完事的,要理解每个参数背后的含义。你想想看,offset 丢了、history 丢了、topic 冲突了,哪个不是线上事故?把这些基础打牢,后面讲高级特性的时候你才能游刃有余。