基于 Flink CDC 的 MySQL → Kafka Streaming ELT 实战

  • 更轻:无需 IDE 与代码工程,YAML 定义即上生产。
  • 更稳:基于 Flink Checkpoint 的端到端语义,天然容错。
  • 更灵:内置路由、分区策略、Topic 映射、格式切换,覆盖 80% 以上实时采集/同步场景。

2. 目标与数据流

3. 环境准备

  • 操作系统:Linux 或 macOS(Windows 建议 WSL2)

  • 已安装:Docker & Docker Compose

  • 需要下载:

    • Flink 1.20.1
    • Flink CDC CLI 3.5.0(解压后包含 bin/ lib/ conf/
    • MySQL、Kafka CDC 连接器 (Jar 放到 Flink CDClib/
    • MySQL Connector/J(Jar 放到 Flink CDClib/--jar 指定)
bash 复制代码
# 1) 下载并解压 Flink
tar -zxvf flink-1.20.1-bin-scala_2.12.tgz
export FLINK_HOME="$PWD/flink-1.20.1"   # 注意:export 而不是 exprot(常见笔误)
cd "$FLINK_HOME"

# 2) 开启 checkpoint(每 3 秒)
cat >> conf/config.yaml <<'YAML'
execution:
  checkpointing:
    interval: 3s
YAML

# 3) 启动
./bin/start-cluster.sh
# 访问 UI: http://localhost:8081

# 如需更多 TaskManager,可以再次执行 start-cluster.sh

生产部署在云上时,如需公网访问 UI,请按需设置 rest.bind-address / rest.address 并配置安全策略。

5. 用 Docker Compose 启动 Kafka/ZooKeeper/MySQL

推荐 :Kafka 使用双监听避免"容器内能连、宿主机连不上"的老问题。

docker-compose.yml

yaml 复制代码
version: "3.8"
services:
  zookeeper:
    image: bitnami/zookeeper:3.9
    container_name: zookeeper
    environment:
      - ALLOW_ANONYMOUS_LOGIN=yes
    ports:
      - "2181:2181"

  kafka:
    image: bitnami/kafka:3.7
    container_name: kafka
    depends_on:
      - zookeeper
    environment:
      - ALLOW_PLAINTEXT_LISTENER=yes
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      # 双监听:容器内用 kafka:9092;宿主机用 localhost:9093
      - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,EXTERNAL://:9093
      - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://localhost:9093
      - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
      - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
    ports:
      - "9092:9092"
      - "9093:9093"

  mysql:
    # Debezium 示例镜像已启用 binlog 与所需权限
    image: debezium/example-mysql:1.1
    container_name: mysql
    environment:
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_USER=mysqluser
      - MYSQL_PASSWORD=mysqlpw
    ports:
      - "3306:3306"

启动并检查:

bash 复制代码
docker compose up -d
docker ps

6. 初始化 MySQL 示例数据

bash 复制代码
docker compose exec mysql mysql -uroot -p123456 -e "
CREATE DATABASE IF NOT EXISTS app_db;
USE app_db;
CREATE TABLE orders   (id INT NOT NULL, price DECIMAL(10,2) NOT NULL, PRIMARY KEY(id));
CREATE TABLE shipments(id INT NOT NULL, city  VARCHAR(255) NOT NULL, PRIMARY KEY(id));
CREATE TABLE products (id INT NOT NULL, product VARCHAR(255) NOT NULL, PRIMARY KEY(id));
INSERT INTO orders    VALUES (1,4.00),(2,100.00);
INSERT INTO shipments VALUES (1,'beijing'),(2,'xian');
INSERT INTO products  VALUES (1,'Beer'),(2,'Cap'),(3,'Peanut');
"
  1. 解压 Flink CDC CLI 3.5.0 到目录(记为 FLINK_CDC_HOME

  2. 将以下 Jar 放入 FLINK_CDC_HOME/lib/

    • MySQL Pipeline Connector 3.5.0
    • Kafka Pipeline Connector 3.5.0
    • MySQL Connector/J(8.x)

切记:CDC 的连接器 Jar 放在 Flink CDC 的 lib 下,不要放到 Flink Home 的 lib(两者不是一个目录)。

8. 编写并提交第一条管道(全库 → 单 Topic)

mysql-to-kafka.yaml

yaml 复制代码
################################################################################
# Sync all MySQL tables in app_db to a single Kafka topic
################################################################################
source:
  type: mysql
  hostname: mysql
  port: 3306
  username: root
  password: 123456
  tables: app_db\..*          # 正则,匹配 app_db 下所有表;注意转义点号
  server-id: 5400-5404        # 多值区间,避免与其他采集端冲突
  server-time-zone: UTC

sink:
  type: kafka
  name: Kafka Sink
  properties.bootstrap.servers: kafka:9092
  topic: yaml-mysql-kafka
  value.format: debezium-json  # 默认;也可 canal-json

pipeline:
  name: MySQL to Kafka Pipeline
  parallelism: 1

提交:

bash 复制代码
cd <FLINK_CDC_HOME>
bash bin/flink-cdc.sh mysql-to-kafka.yaml
# 控制台会打印 Job ID;Flink UI 可见运行中的作业

9. 验证消费

  • 容器内消费:
bash 复制代码
docker compose exec kafka kafka-console-consumer.sh \
  --bootstrap-server kafka:9092 \
  --topic yaml-mysql-kafka --from-beginning
  • 宿主机消费(如本地装了 Kafka CLI):
bash 复制代码
kafka-console-consumer --bootstrap-server localhost:9093 \
  --topic yaml-mysql-kafka --from-beginning

你将看到 debezium-json 结构(before/after/op/source 等)。例如 insert:

json 复制代码
{
  "before": null,
  "after": { "id": 1, "price": 4.00 },
  "op": "c",
  "source": { "db": "app_db", "table": "orders" }
}

需要 ts_ms 等更多元数据?可在 MySQL source 开启 metadata.list 暴露(进阶可选)。

10. 实时变更与模式演进演示

在 MySQL 中执行(容器内):

sql 复制代码
INSERT INTO app_db.orders (id, price) VALUES (3, 100.00);

ALTER TABLE app_db.orders ADD amount VARCHAR(100) NULL;

UPDATE app_db.orders SET price=100.00, amount='100.00' WHERE id=1;

DELETE FROM app_db.orders WHERE id=2;

Kafka 消费端会实时看到 c/u/d 事件;新增列 amount 也会随模式演进传播到下游。

11. 进阶一:按表路由(含分片表合并)

将上游多表,路由为不同 Kafka 主题;或将分片表 合并到一个主题

yaml 复制代码
route:
  - source-table: app_db.orders
    sink-table: kafka_ods_orders

  - source-table: app_db.shipments
    sink-table: kafka_ods_shipments

  - source-table: app_db.products
    sink-table: kafka_ods_products

  # 分片合并示例:app_db.order01 / order02 / ... → 同一主题
  - source-table: app_db.order\.*
    sink-table: kafka_ods_orders

查看已创建 Topic:

bash 复制代码
docker compose exec kafka kafka-topics.sh \
  --bootstrap-server kafka:9092 --list

12. 进阶二:多分区写入策略

sink.partition.strategy 支持:

  • all-to-zero(默认):全部写入分区 0
  • hash-by-key:按主键哈希分布到不同分区

示例:

yaml 复制代码
sink:
  type: kafka
  properties.bootstrap.servers: kafka:9092
  topic: yaml-mysql-kafka-hash-by-key
  partition.strategy: hash-by-key

先创建多分区主题(容器内):

bash 复制代码
docker compose exec kafka kafka-topics.sh \
  --create --topic yaml-mysql-kafka-hash-by-key \
  --bootstrap-server kafka:9092 --partitions 12

13. 进阶三:表到主题映射(不做 Schema 合并)

route 不同,TableId-to-Topic 映射 不会合并不同表的 Schema,只是把不同表分派到不同 Topic

yaml 复制代码
sink:
  type: kafka
  properties.bootstrap.servers: kafka:9092
  sink.tableId-to-topic.mapping: |
    app_db.orders:yaml-mysql-kafka-orders;
    app_db.shipments:yaml-mysql-kafka-shipments;
    app_db.products:yaml-mysql-kafka-products

14. 进阶四:输出格式切换

  • value.format: debezium-json(默认):before/after/op/source 等字段
  • value.format: canal-jsonold/data/type/database/table/pkNames 等字段

canal-json insert 示例:

json 复制代码
{
  "old": null,
  "data": [{ "id": 1, "price": 100, "amount": "100.00" }],
  "type": "INSERT",
  "database": "app_db",
  "table": "orders",
  "pkNames": ["id"]
}

15. 常见问题(踩坑手册)

  1. Host 连不通 Kafka

    • 使用双监听 :容器用 kafka:9092,宿主机用 localhost:9093
    • 千万别把 bootstrap.servers 配成 0.0.0.0
  2. CDC 连接器未生效 / ClassNotFound

    • CDC MySQL/Kafka 连接器 JarFlink CDC 的 lib/
    • 还需要 MySQL Connector/J
  3. 正则匹配不到表

    • 匹配全库正确写法:app_db\..*(点号要转义)。
  4. server-id 冲突

    • 多个采集端连同一 MySQL 时,给不同的 server-id 区间。
  5. 时区/时间戳错乱

    • 指定 server-time-zone(如 UTC),并结合下游消费侧统一时区。
  6. Topic 自动创建与权限

    • 默认会自动创建;生产建议显式创建并配置权限、分区数、副本数、压缩与保留策略。
  7. Checkpoint 间隔

    • 开发环境 3s 没问题;生产需根据延迟、吞吐、Kafka 事务/幂等等综合评估。

16. 生产化清单(建议)

  • Flink 侧

    • Restart 策略、Checkpoint 存储(如 HDFS/S3)、并行度与资源配额
    • Savepoint 升级流程与回滚演练
  • Kafka 侧

    • Topic 副本数、分区数、保留策略(retention.ms/bytes
    • 压缩(compression.type),幂等/事务(Producer 端)
    • ACL、鉴权、网络与磁盘监控
  • MySQL 侧

    • binlog 保留、写入压力、主从延迟、热点表索引优化
    • DDL 规范(列类型兼容、避免频繁 DDL 抖动)
  • 可观测性

    • Flink Metrics、GC、背压(Backpressure)
    • Kafka Lag、Topic QPS、ISR 变化
  • 变更治理

    • 路由/映射规则变更审批与回归测试
    • 下游 Schema 演进策略(兼容列、空值、默认值)

17. 清理环境

bash 复制代码
# 关闭 Kafka/ZK/MySQL
docker compose down

# 停止 Flink
"$FLINK_HOME"/bin/stop-cluster.sh
相关推荐
L.EscaRC3 小时前
浅析MySQL InnoDB存储引擎的MVCC实现原理
数据库·mysql
-指短琴长-10 小时前
MySQL快速入门——基本查询(下)
android·mysql·adb
Hello.Reader10 小时前
用 CdcUp CLI 一键搭好 Flink CDC 演练环境
大数据·flink
Hello.Reader11 小时前
Flink CDC「Data Pipeline」定义与参数速查
大数据·flink
August_._11 小时前
【MySQL】SQL语法详细总结
java·数据库·后端·sql·mysql·oracle
林北北的霸霸13 小时前
django初识与安装
android·mysql·adb
想睡hhh14 小时前
mysql内置函数——了解常用的函数
mysql
教练、我想打篮球15 小时前
05 kafka 如何存储较大数据记录
java·kafka·record
Archy_Wang_115 小时前
centos7的mysql做定时任务备份所有数据库
数据库·mysql