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 的流向标清楚了:
3.4 配置最佳实践
最后,分享几个我在实战中沉淀下来的配置习惯:
- offset 和 history 用不同的 Kafka topic。虽然可以共用,但分开更清晰,排查问题也方便。
- 给每个 Connector 单独一个
database.server.name,不要复用。否则 topic 会冲突,数据乱掉。 - 生产环境用分布式模式,至少 3 个 Worker 节点。单机模式只适合本地调试。
- 监控 offset 的 lag。如果 offset 写入太慢,说明 Kafka 集群压力大,需要扩容。
有一次我把
offset.flush.interval.ms 设成了 300000(5分钟),结果 Connector 挂了之后重启,丢了 4 分 50 秒的变更事件。后来改成 5000(5秒),虽然写入频繁了一点,但至少不会丢太多数据。这个值要根据你的业务容忍度来调。
嗯,部署模式和配置项就聊到这。记住一句话:配置不是写出来就完事的,要理解每个参数背后的含义。你想想看,offset 丢了、history 丢了、topic 冲突了,哪个不是线上事故?把这些基础打牢,后面讲高级特性的时候你才能游刃有余。