实时数据源接入: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

整体架构图

下面这张图展示了三种数据源接入实时管道的完整链路:

实时数据源接入架构 MySQL Binlog PostgreSQL WAL Kafka Topic Canal / Maxwell Debezium / 逻辑复制 Kafka Connect Kafka 消息队列(中枢) Flink 实时计算 Spark Streaming 数据湖 / Lakehouse 说明:Binlog/WAL 通过 CDC 工具写入 Kafka,下游从 Kafka 消费,实现解耦与缓冲

从这张图能看出来,Kafka是整个管道的「中枢」。Binlog和WAL的数据先进Kafka,下游再各取所需。这样做的好处是——数据库只写一次,下游随便加,不会对源库造成压力。

我的建议:刚开始搭建时,别想着一步到位。先跑通一条链路——比如MySQL → Canal → Kafka → Flink。验证没问题了,再逐步接入其他数据源。我曾经在一个项目里同时接5个数据源,结果出问题都不知道从哪查起……

好了,三种数据源的原理和配置就讲到这里。记住一句话:Binlog和WAL是「源头活水」,Kafka是「蓄水池」,三者配合才能构建稳定可靠的实时管道。