实时数据源接入:MySQL Binlog、PostgreSQL WAL、Kafka消息队列的原理与配置
做实时管道,第一步就是搞定数据源。说白了,你得知道数据从哪来,怎么把它「拽」出来,还不能影响源系统的正常运转。
我这些年接手的项目,十有八九的实时数据都来自三个地方:MySQL、PostgreSQL,还有Kafka。今天咱们就把这三个东西的原理和配置掰开揉碎了讲清楚。
MySQL Binlog:最成熟的变更捕获方案
MySQL的Binlog,全称是Binary Log。它记录的是数据库里所有「写」操作——INSERT、UPDATE、DELETE,还有DDL。你想想看,只要开了Binlog,就等于有了数据库的完整操作录像带。
核心原理:Binlog有三种格式,我建议你直接记这个结论——生产环境用ROW格式。
| 格式 | 记录内容 | 适用场景 |
|---|---|---|
| STATEMENT | 记录SQL语句本身 | 简单复制,但主从数据可能不一致 |
| ROW | 记录每一行数据的变化前后值 | 实时同步、数据审计(推荐) |
| MIXED | 混合模式,自动选择 | 兼顾性能与一致性 |
我在项目中遇到过一个问题:某团队用STATEMENT格式做CDC,结果因为一个NOW()函数,主从数据对不上。排查了整整两天,最后改成ROW格式才解决。嗯,这里要注意——ROW格式虽然日志体积大,但换来的是准确。
配置要点
开启Binlog其实就几个参数。我习惯在my.cnf里这样配:
[mysqld]
server_id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_row_image = FULL
expire_logs_days = 7
max_binlog_size = 1G
这里有个坑:binlog_row_image默认是FULL,记录整行数据。如果你只关心变更的列,可以改成MINIMAL,日志量能减少30%-50%。但要注意——如果下游需要完整的前后镜像做比对,FULL才是安全的。
我的经验:刚开始做CDC时,建议先用FULL。等跑稳定了,再根据实际需求优化。我曾经因为贪图性能直接上MINIMAL,结果业务方要查某字段的历史变更,我拿不出来……从那以后,我都是先保数据完整性。
PostgreSQL WAL:更精细的日志解析
PostgreSQL的WAL(Write-Ahead Log)和MySQL的Binlog思路类似,但实现更「硬核」。WAL是PG保证数据一致性的核心机制——任何数据修改,必须先写WAL,再写数据文件。
做实时管道时,我们通常通过逻辑复制来消费WAL。PG的逻辑复制从10.0版本开始原生支持,不需要第三方工具。
逻辑复制配置
第一步,修改postgresql.conf:
wal_level = logical
max_replication_slots = 5
max_wal_senders = 5
第二步,创建发布者(Publisher):
CREATE PUBLICATION my_pub FOR ALL TABLES;
第三步,在订阅端创建订阅(Subscriber):
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=pg-primary port=5432 dbname=mydb user=replicator password=xxx'
PUBLICATION my_pub;
你看,PG的逻辑复制配置比MySQL简洁不少。但有个限制——逻辑复制只能复制DML,DDL变更不会自动同步。我记得有个项目,业务方频繁加字段,结果订阅端一直报错,最后只能手动同步DDL。
避坑指南:我曾经在生产环境直接对主库执行ALTER TABLE ADD COLUMN,结果订阅端因为表结构不一致直接断开了复制。从那以后,我养成了一个习惯——DDL变更前,先暂停订阅,改完再恢复。
Kafka消息队列:实时管道的「中枢神经」
Kafka在实时管道里的角色,说白了就是「缓冲+解耦」。Binlog和WAL是数据源,Kafka是中间层,下游的Flink、Spark、数据湖都从Kafka消费。
为什么非要用Kafka?我直接说结论:
- 削峰填谷:数据库的写入峰值可能很高,Kafka能扛住百万级TPS
- 多消费者:一份数据可以被多个下游同时消费,互不影响
- 回溯消费:数据保留7天,出问题了可以重放
核心概念速览
| 概念 | 说明 | 类比 |
|---|---|---|
| Topic | 消息的分类,类似数据库的表 | 一个Topic对应一类事件 |
| Partition | Topic的分片,并行消费的基础 | 类似分库分表的分片 |
| Offset | 消息在分区内的位置 | 类似数据库的行号 |
| Consumer Group | 消费者的逻辑分组 | 一个组内消费者分摊分区 |
生产者配置示例
我一般用Java客户端,配置如下:
Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092,kafka2:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("acks", "all"); // 等待所有副本确认
props.put("retries", 3); // 重试次数
props.put("linger.ms", 5); // 延迟发送,提高吞吐
props.put("batch.size", 16384); // 批量大小
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
关键参数:acks=all保证数据不丢,但延迟会高一点。如果业务能接受少量丢失,可以改成acks=1,吞吐能提升30%以上。
三种数据源的对比与选型
我整理了一张对比表,方便你快速决策:
| 维度 | MySQL Binlog | PostgreSQL WAL | Kafka |
|---|---|---|---|
| 数据源类型 | 数据库变更日志 | 数据库变更日志 | 消息中间件 |
| 捕获方式 | 模拟Slave拉取 | 逻辑复制/解码插件 | 生产者主动推送 |
| 延迟 | 毫秒级 | 毫秒级 | 毫秒级 |
| 数据完整性 | 高(ROW格式) | 高(WAL保证) | 取决于配置 |
| 典型工具 | Canal、Maxwell | Debezium、PG自身逻辑复制 | Kafka Connect、自研Producer |
选型时我一般这么判断:
- 如果源是MySQL,优先用Canal + Kafka的组合
- 如果源是PostgreSQL 10+,直接用逻辑复制,省掉中间件
- 如果已经有Kafka生态,直接用Kafka Connect做CDC
整体架构图
下面这张图展示了三种数据源接入实时管道的完整链路:
从这张图能看出来,Kafka是整个管道的「中枢」。Binlog和WAL的数据先进Kafka,下游再各取所需。这样做的好处是——数据库只写一次,下游随便加,不会对源库造成压力。
我的建议:刚开始搭建时,别想着一步到位。先跑通一条链路——比如MySQL → Canal → Kafka → Flink。验证没问题了,再逐步接入其他数据源。我曾经在一个项目里同时接5个数据源,结果出问题都不知道从哪查起……
好了,三种数据源的原理和配置就讲到这里。记住一句话:Binlog和WAL是「源头活水」,Kafka是「蓄水池」,三者配合才能构建稳定可靠的实时管道。