4、Debezium实战:MySQL Binlog监听、Debezium Server配置、Kafka Connect集成
这一章我们直接动手。理论讲再多,不如跑通一条数据流来得实在。
Debezium 是什么?说白了,它就是数据库的「眼线」。它能盯着 MySQL 的 Binlog,一有变化就往外推。我最早接触它是在 2019 年,那时候公司要从 MySQL 实时同步数据到 Elasticsearch,试了几种方案都不太满意,直到遇到 Debezium——嗯,这玩意儿确实靠谱。
4.1 MySQL Binlog 监听原理
先搞清楚一件事:MySQL 怎么知道数据变了?答案是 Binlog。
Binlog(Binary Log)记录了所有数据变更操作。INSERT、UPDATE、DELETE 都会写进去。Debezium 做的事情,就是伪装成一个 MySQL Slave,去读取 Binlog 的变更事件。
我个人习惯把 Binlog 的格式设为 ROW。为什么?因为只有 ROW 模式才能记录每一行数据变更前后的完整内容。STATEMENT 模式只记 SQL,MIXED 模式在某些场景下会丢字段细节——我在项目中踩过这个坑,查了半天才发现是 Binlog 格式的问题。
binlog_row_image=FULL,这样 Debezium 才能拿到完整的 before/after 数据。
配置 Binlog 的步骤很简单,在 MySQL 的 my.cnf 里加上这几行:
[mysqld]
server-id = 223344
log_bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL
expire_logs_days = 7
然后重启 MySQL。验证是否生效:
SHOW VARIABLES LIKE 'log_bin%';
SHOW VARIABLES LIKE 'binlog_format%';
看到 log_bin = ON 和 binlog_format = ROW,就说明配置成功了。
4.2 Debezium Server 配置与启动
Debezium 有两种部署方式:独立 Server 和 Kafka Connect 插件。我们先讲 Server 模式,因为它最简单,适合快速验证。
下载 Debezium Server 的压缩包,解压后你会看到 conf 目录。核心配置文件是 application.properties。我贴一份我常用的配置:
debezium.sink.type=kafka
debezium.sink.kafka.producer.bootstrap.servers=localhost:9092
debezium.sink.kafka.producer.key.serializer=org.apache.kafka.common.serialization.StringSerializer
debezium.sink.kafka.producer.value.serializer=org.apache.kafka.common.serialization.StringSerializer
debezium.source.connector.class=io.debezium.connector.mysql.MySqlConnector
debezium.source.offset.storage.file.filename=/data/debezium/offsets.dat
debezium.source.database.hostname=192.168.1.100
debezium.source.database.port=3306
debezium.source.database.user=debezium_user
debezium.source.database.password=debezium_pass
debezium.source.database.server.id=12345
debezium.source.database.server.name=my-mysql-instance
debezium.source.database.include.list=mydb
debezium.source.table.include.list=mydb.orders,mydb.users
debezium.source.snapshot.mode=initial
这里有几个关键点:
- debezium.source.snapshot.mode:我建议第一次用
initial,它会先做一次全量快照,然后切换到增量监听。后续重启可以用schema_only或when_needed。 - offset.storage.file.filename:记录消费位置的偏移量文件。如果丢了,Debezium 会重新拉全量——我经历过一次,几百万数据重新同步,差点把 MySQL 打挂。
- database.server.name:这个值会作为 Kafka Topic 的前缀。比如这里配了
my-mysql-instance,那么 Topic 名就是my-mysql-instance.mydb.orders。
启动命令很简单:
./bin/run.sh
看日志,如果出现 Connected to MySQL 和 Snapshot completed,就说明跑起来了。
Access denied for user,原因是 MySQL 用户权限不够。Debezium 需要 REPLICATION SLAVE、REPLICATION CLIENT 和 SELECT 权限。记得用这条 SQL 授权:GRANT REPLICATION SLAVE, REPLICATION CLIENT, SELECT ON *.* TO 'debezium_user'@'%';
4.3 Kafka Connect 集成
Server 模式适合测试,但生产环境我更推荐用 Kafka Connect。为什么?因为它有 REST API、支持动态扩缩容、还能和 Kafka 生态无缝集成。
Kafka Connect 分两种模式:Standalone 和 Distributed。我直接说结论——用 Distributed。Standalone 只适合单机测试,生产环境一旦挂了就是单点故障。
配置步骤分三步:
- 把 Debezium 插件放到 Kafka Connect 的插件目录
- 配置 connect-distributed.properties
- 通过 REST API 提交连接器
先看插件部署。下载 Debezium MySQL Connector 的插件包,解压后放到 Kafka 的 plugins 目录:
mkdir -p /opt/kafka/plugins/debezium
tar -xzf debezium-connector-mysql-2.5.0.Final-plugin.tar.gz -C /opt/kafka/plugins/debezium
然后在 connect-distributed.properties 里指定插件路径:
plugin.path=/opt/kafka/plugins/debezium
bootstrap.servers=localhost:9092
group.id=debezium-connect-cluster
key.converter=org.apache.kafka.connect.json.JsonConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
key.converter.schemas.enable=false
value.converter.schemas.enable=false
offset.storage.topic=connect-offsets
config.storage.topic=connect-configs
status.storage.topic=connect-status
启动 Kafka Connect:
./bin/connect-distributed.sh config/connect-distributed.properties
最后,通过 REST API 提交连接器配置。我个人习惯用 curl 或者 Postman:
curl -X POST http://localhost:8083/connectors \
-H "Content-Type: application/json" \
-d '{
"name": "mysql-orders-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "192.168.1.100",
"database.port": "3306",
"database.user": "debezium_user",
"database.password": "debezium_pass",
"database.server.id": "12346",
"database.server.name": "mysql-orders",
"database.include.list": "mydb",
"table.include.list": "mydb.orders",
"snapshot.mode": "initial",
"topic.prefix": "mysql-orders"
}
}'
返回 201 Created 就成功了。你可以用 GET /connectors 查看所有连接器,用 GET /connectors/mysql-orders-connector/status 查看运行状态。
rest.port=8084。另外,生产环境建议开启认证,不然谁都能往你的集群里塞连接器。
4.4 数据格式与 Topic 结构
数据进入 Kafka 后,长什么样?我截一条实际的消息给你看:
{
"schema": { ... },
"payload": {
"before": null,
"after": {
"id": 1024,
"order_no": "ORD20241001",
"amount": 299.00,
"status": "PAID",
"create_time": 1727712000000
},
"source": {
"version": "2.5.0.Final",
"connector": "mysql",
"name": "mysql-orders",
"ts_ms": 1727712001000,
"snapshot": "false",
"db": "mydb",
"table": "orders",
"server_id": 223344
},
"op": "c",
"ts_ms": 1727712001002
}
}
重点看 op 字段:
| 操作 | op 值 | 说明 |
|---|---|---|
| CREATE | c | 新增记录 |
| UPDATE | u | 更新记录,before 和 after 都有值 |
| DELETE | d | 删除记录,after 为 null |
| READ | r | 全量快照阶段的数据 |
Topic 的命名规则是 {topic.prefix}.{database}.{table}。比如上面配置的 mysql-orders.mydb.orders。每个表一个 Topic,结构清晰。
4.5 整体架构图
下面这张图展示了本章搭建的完整数据流:
4.6 常见问题与排查思路
跑通这条链路不难,但生产环境总会遇到各种幺蛾子。我列几个我踩过的坑:
- 连接器状态显示 FAILED:先看日志。最常见的原因是 MySQL 连接超时,或者 Binlog 位置被清理了。检查
expire_logs_days配置,我建议至少保留 3 天。 - 数据延迟很大:检查 Kafka 的生产者配置。
acks=1和linger.ms=0能降低延迟。如果还不行,看看 MySQL 的 Binlog 写入频率是不是太高了。 - Topic 里出现大量重复数据:这通常是 offset 提交失败导致的。Kafka Connect 的
offset.storage.topic如果分区数不够,或者副本数不足,就会丢 offset。我一般给这个 Topic 配 25 个分区、3 副本。 - DDL 变更导致连接器挂掉:Debezium 对 DDL 的支持有限。如果表结构变了,比如新增字段,连接器可能会报错。我建议在变更表结构前,先暂停连接器,改完再重启。
kafka-console-consumer 直接消费 Topic,能最快看到数据格式是否正确。命令:./bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic mysql-orders.mydb.orders --from-beginning
嗯,这一章的内容就到这里。你动手跑一遍,把 MySQL 的 Binlog 打开,启动 Debezium,再往表里插几条数据,看看 Kafka 里能不能收到消息。能收到,你就掌握了 CDC 实时同步最核心的一环。