对于交易数据写到mysql数据库,使用Canal监听数据库binlog日志,然后将监听到的日志信息发送到kafka,flink消费Kafka数据,整个流程的场景。以国内最常用的 Canal(MySQL CDC) + Kafka 为例,从环境部署、配置、完整流程、代码、联调全链路落地,同时补充 Flink CDC 直连方案(无中间Kafka),覆盖两种主流工业实现。
一、整体架构(Canal + MySQL + Kafka)
业务系统 → MySQL(开启binlog) → Canal 监听binlog → Canal转发数据到Kafka → Flink消费Kafka计算大屏指标
核心:Canal 伪装成 MySQL 从库,同步 binlog 日志,解析后推送 Kafka,零业务代码侵入。
二、前置环境准备
1. MySQL 开启 Binlog(必做)
1.1 修改 MySQL 配置 my.cnf / my.ini
ini
[mysqld]
# 开启binlog
log-bin=mysql-bin
# binlog 格式,CDC 必须用 ROW 模式
binlog-format=ROW
# 服务器ID,集群内唯一
server-id=1
# 只同步指定库(可选,过滤无关库)
binlog-do-db=trade_db
重启 MySQL:
bash
# Linux
systemctl restart mysqld
# Windows 重启MySQL服务
1.2 创建 Canal 专属账号(最小权限)
登录 MySQL 执行:
sql
-- 创建账号
CREATE USER 'canal'@'%' IDENTIFIED BY 'Canal@123456';
-- 授予复制权限
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
1.3 验证 Binlog 开启
sql
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_format';
结果均为 ON / ROW 即正常。
2. 部署 Canal
2.1 下载&解压
下载地址:https://github.com/alibaba/canal/releases
选用稳定版 canal.deployer-1.1.7.tar.gz
bash
tar -zxvf canal.deployer-1.1.7.tar.gz -C /usr/local/
cd /usr/local/canal
2.2 核心配置文件
① 全局配置 conf/canal.properties
配置 Canal 服务、对接 Kafka:
properties
# canal 服务端口
canal.port = 11111
# 模式:kafka 代表数据投递到Kafka
canal.serverMode = kafka
# ========== Kafka 集群地址 ==========
kafka.bootstrap.servers = 127.0.0.1:9092
# 消息分区策略
kafka.partition.number = 1
② 实例配置 conf/example/instance.properties
example 是默认实例,负责监听指定 MySQL 库表:
properties
# ========== MySQL 连接信息 ==========
canal.instance.master.address=127.0.0.1:3306
canal.instance.master.journal.name=
canal.instance.master.position=
canal.instance.master.timestamp=
# MySQL 账号密码(上面创建的canal账号)
canal.instance.dbUsername=canal
canal.instance.dbPassword=Canal@123456
canal.instance.defaultDatabaseName=trade_db
# 字符集
canal.instance.connectionCharset = UTF-8
# ========== 过滤规则:只监听交易表 trade_record ==========
# 格式:库名.表名,正则匹配
canal.instance.filter.regex=trade_db\\.trade_record
# ========== 投递到Kafka的主题名 ==========
canal.mq.topic=ods_trade_cdc_topic
# 单分区
canal.mq.partition=0
2.3 启动 Canal
bash
# Linux
bin/startup.sh
# 查看日志,验证启动
tail -f logs/canal/canal.log
无报错、正常监听即部署完成。
3. 准备测试表 & 测试数据
在 MySQL trade_db 创建交易表:
sql
USE trade_db;
CREATE TABLE trade_record (
trade_id VARCHAR(32) PRIMARY KEY COMMENT '交易ID',
cust_id VARCHAR(20) COMMENT '客户ID',
trade_type VARCHAR(10) COMMENT '交易类型',
trade_amount DECIMAL(16,2) COMMENT '交易金额',
trade_time DATETIME COMMENT '交易时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='交易流水表';
三、链路验证:MySQL 增数据 → Canal → Kafka
- 打开 Kafka 控制台消费者,监听
ods_trade_cdc_topic
bash
kafka-console-consumer.sh --topic ods_trade_cdc_topic --bootstrap-server 127.0.0.1:9092
- MySQL 插入一条交易数据:
sql
INSERT INTO trade_record(trade_id,cust_id,trade_type,trade_amount,trade_time)
VALUES('T001','cust001','买入',5200.00,NOW());
- 观察 Kafka 控制台,会收到 Canal 标准 JSON 格式 CDC 数据,示例:
json
{
"data": [
{
"trade_id": "T001",
"cust_id": "cust001",
"trade_type": "买入",
"trade_amount": "5200.00",
"trade_time": "2026-06-10 16:20:00"
}
],
"database": "trade_db",
"es": 1718026800000,
"id": 1,
"isDdl": false,
"mysqlType": {
"trade_id": "varchar(32)",
"cust_id": "varchar(20)",
"trade_type": "varchar(10)",
"trade_amount": "decimal(16,2)",
"trade_time": "datetime"
},
"old": null,
"sql": "",
"table": "trade_record",
"ts": 1718026800123,
"type": "INSERT"
}
字段说明:
type:操作类型INSERT/UPDATE/DELETEdata:变更后的行数据数组database/table:库表名
到这里:MySQL → Canal → Kafka 链路完全通了。
四、代码层:Flink 消费 Kafka-CDC 数据,计算实时交易总额
承接上面的 CDC 数据,Flink 消费、解析、窗口聚合、输出大屏指标,完整可运行代码。
4.1 补充 Maven 依赖(沿用之前项目)
xml
<!-- Flink Kafka 连接器 -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka</artifactId>
<version>1.17.0</version>
<scope>provided</scope>
</dependency>
<!-- JSON 解析 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
4.2 Flink 完整代码
java
package com.sec.flink.cdc;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.connector.kafka.source.KafkaSource;
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.WindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;
import java.time.Duration;
/**
* 链路:MySQL Binlog -> Canal -> Kafka -> Flink 实时计算交易总额(大屏使用)
*/
public class CanalCdcKafkaJob {
public static void main(String[] args) throws Exception {
// 1. 初始化流环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 生产环境开启Checkpoint,保证数据不丢不重
// env.enableCheckpointing(30000);
// 2. 构建Kafka Source,消费Canal投递的CDC数据
KafkaSource<String> kafkaSource = KafkaSource.<String>builder()
.setBootstrapServers("127.0.0.1:9092")
.setTopics("ods_trade_cdc_topic") // Canal 对应的Kafka主题
.setGroupId("flink-cdc-group")
.setStartingOffsets(OffsetsInitializer.latest())
.setValueDeserializer(new SimpleStringSchema())
.build();
DataStream<String> kafkaStream = env.fromSource(
kafkaSource,
WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(2)),
"Canal-CDC-Source"
);
// 3. 解析Canal JSON,提取交易金额(只处理INSERT新增交易)
DataStream<Double> amountStream = kafkaStream.map(line -> {
JSONObject root = JSONObject.parseObject(line);
// 只处理新增交易,UPDATE/DELETE 过滤
String opType = root.getString("type");
if (!"INSERT".equalsIgnoreCase(opType)) {
return 0.0;
}
// 获取行数据数组
JSONArray dataArray = root.getJSONArray("data");
if (dataArray == null || dataArray.isEmpty()) {
return 0.0;
}
// 取出单条交易数据
JSONObject tradeData = dataArray.getJSONObject(0);
String amountStr = tradeData.getString("trade_amount");
return Double.parseDouble(amountStr);
}).filter(amount -> amount > 0); // 过滤无效0值
// 4. 5秒滚动窗口:计算实时交易总额(大屏核心指标)
DataStream<String> resultStream = amountStream
.window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
.apply(new WindowFunction<Double, String, String, TimeWindow>() {
@Override
public void apply(String key, TimeWindow window,
Iterable<Double> values, Collector<String> out) {
double total = 0.0;
for (Double val : values) {
total += val;
}
String windowTime = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(window.getEnd());
String result = String.format("窗口时间:%s , 实时交易总额:%.2f 元", windowTime, total);
out.collect(result);
}
});
// 本地控制台输出,调试用
resultStream.print("【CDC实时交易总额】");
// 生产环境:此处添加 Doris Sink,写入结果库供大屏查询
// resultStream.sinkTo(dorisSink);
env.execute("Canal-CDC-Trade-Total-Job");
}
}
4.3 联调测试全流程
- 确保 MySQL、Canal、Kafka 全部启动;
- 启动上述 Flink 程序;
- 在 MySQL 执行
INSERT新增交易记录; - Flink 控制台每5秒输出当前窗口交易总额;
- 生产环境开启 Doris Sink,结果入库后,前端大屏轮询查询展示。
五、进阶方案:Flink CDC(直连MySQL,无Canal、无中间Kafka)
如果想精简架构 ,不用独立 Canal 服务,直接使用 Flink CDC 连接器 监听 MySQL binlog,一步到位,也是当下主流方案。
5.1 新增 Maven 依赖
xml
<!-- Flink CDC MySQL 连接器 -->
<dependency>
<groupId>com.ververica</groupId>
<artifactId>flink-connector-mysql-cdc</artifactId>
<version>2.4.0</version>
<scope>provided</scope>
</dependency>
5.2 Flink CDC 直连 MySQL 完整代码
java
package com.sec.flink.cdc;
import com.alibaba.fastjson.JSONObject;
import com.ververica.cdc.connectors.mysql.source.MySqlSource;
import com.ververica.cdc.connectors.mysql.table.StartupOptions;
import com.ververica.cdc.debezium.JsonDebeziumDeserializationSchema;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.WindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;
public class FlinkCdcDirectJob {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 1. 构建 Flink MySQL CDC Source
MySqlSource<String> mysqlSource = MySqlSource.<String>builder()
.hostname("127.0.0.1")
.port(3306)
.databaseList("trade_db") // 监听库
.tableList("trade_db.trade_record") // 监听表
.username("canal")
.password("Canal@123456")
// 启动策略:latest 从当前最新binlog开始读取
.startupOptions(StartupOptions.latest())
// 序列化:转为JSON字符串
.deserializer(new JsonDebeziumDeserializationSchema())
.build();
// 2. 读取CDC流
DataStream<String> cdcStream = env.fromSource(
mysqlSource,
WatermarkStrategy.noWatermarks(),
"Flink-MySQL-CDC"
);
// 3. 解析Debezium格式JSON,提取交易金额
DataStream<Double> amountStream = cdcStream.map(line -> {
JSONObject json = JSONObject.parseObject(line);
// 操作类型:c=新增, u=更新, d=删除
String op = json.getString("op");
if (!"c".equals(op)) {
return 0.0;
}
// after 字段:变更后的数据
JSONObject after = json.getJSONObject("after");
String amountStr = after.getString("trade_amount");
return Double.parseDouble(amountStr);
}).filter(amt -> amt > 0);
// 4. 窗口聚合计算总额
DataStream<String> resultStream = amountStream
.window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
.apply(new WindowFunction<Double, String, String, TimeWindow>() {
@Override
public void apply(String key, TimeWindow window,
Iterable<Double> values, Collector<String> out) {
double total = 0;
for (Double v : values) total += v;
String time = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(window.getEnd());
out.collect(time + " | 实时交易总额:" + String.format("%.2f", total));
}
});
resultStream.print("【Flink-CDC直连 交易总额】");
env.execute("Flink-CDC-Direct-Job");
}
}
5.3 架构对比
-
Canal + Kafka + Flink
- 优点:解耦强、缓冲流量、可多消费端复用Kafka数据(大屏、风控、数仓共用)、运维成熟;
- 适用:中大型交易集群、多业务复用数据流(生产主流)。
-
Flink CDC 直连 MySQL
- 优点:架构极简、组件少、部署简单、延迟更低;
- 缺点:数据流无法复用,Flink 挂掉则数据断流;
- 适用:单一实时任务、小型系统、快速落地。
六、生产环境关键优化 & 可靠性(必看)
- 断点续传
- Canal 自动记录 binlog 偏移量,重启不丢数据;
- Flink 开启 Checkpoint,消费 Kafka/CDC 均支持状态恢复。
- 幂等性
利用trade_id交易唯一ID做去重,防止重复计算。 - 过滤无用操作
交易场景只处理INSERT,过滤UPDATE/DELETE,减少计算压力。 - 并行度匹配
Canal 分区数 = Kafka 分区数 = Flink 并行度,避免数据倾斜。 - 权限最小化
MySQL 账号只授予复制、查询权限,禁止高权限。
七、总结
- Canal 方案 :部署独立 Canal 服务监听 MySQL binlog,转发到 Kafka,Flink 消费 Kafka,企业金融交易首选,多业务复用数据流;
- Flink CDC 直连 :无需中间组件,Flink 直接监听 binlog,架构简单,适合单一任务;
- 核心本质:两种方案都是基于 MySQL binlog 日志实现变更数据捕获,不侵入业务代码、数据库压力极小,是实时交易大屏标准接入方式。