作者:羅宇俠
本篇教程將展示如何使用 Flink CDC 構建實時資料湖,並處理分庫分表合併同步的場景。
Flink-CDC 專案地址:
Flink 中文學習網站
https://flink-learning.org.cn
在 OLTP 系統中,為了解決單表資料量大的問題,通常採用分庫分表的方式將單個大表進行拆分以提高系統的吞吐量。
但是為了方便資料分析,通常需要將分庫分表拆分出的表在同步到資料倉儲、資料湖時,再合併成一個大表。
這篇教程將展示如何使用 Flink CDC 構建實時資料湖來應對這種場景,本教程的演示基於 Docker,只涉及 SQL,無需一行 Java/Scala 程式碼,也無需安裝 IDE,你可以很方便地在自己的電腦上完成本教程的全部內容。
接下來將以資料從 MySQL 同步到 Iceberg [1] 為例展示整個流程,架構圖如下所示:
一、準備階段
準備一臺已經安裝了 Docker 的 Linux 或者 MacOS 電腦。
1.1 準備教程所需要的元件
接下來的教程將以 docker-compose
的方式準備所需要的元件。
使用下面的內容建立一個 docker-compose.yml
檔案:
version: '2.1'
services:
sql-client:
user: flink:flink
image: yuxialuo/flink-sql-client:1.13.2.v1
depends_on:
- jobmanager
- mysql
environment:
FLINK_JOBMANAGER_HOST: jobmanager
MYSQL_HOST: mysql
volumes:
- shared-tmpfs:/tmp/iceberg
jobmanager:
user: flink:flink
image: flink:1.13.2-scala_2.11
ports:
- "8081:8081"
command: jobmanager
environment:
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: jobmanager
volumes:
- shared-tmpfs:/tmp/iceberg
taskmanager:
user: flink:flink
image: flink:1.13.2-scala_2.11
depends_on:
- jobmanager
command: taskmanager
environment:
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: jobmanager
taskmanager.numberOfTaskSlots: 2
volumes:
- shared-tmpfs:/tmp/iceberg
mysql:
image: debezium/example-mysql:1.1
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_USER=mysqluser
- MYSQL_PASSWORD=mysqlpw
volumes:
shared-tmpfs:
driver: local
driver_opts:
type: "tmpfs"
device: "tmpfs"
該 Docker Compose 中包含的容器有:
- SQL-Client:Flink SQL Client, 用來提交 SQL 查詢和檢視 SQL 的執行結果;
- Flink Cluster:包含 Flink JobManager 和 Flink TaskManager,用來執行 Flink SQL;
- MySQL:作為分庫分表的資料來源,儲存本教程的
user
表。
在 docker-compose.yml
所在目錄下執行下面的命令來啟動本教程需要的元件:
docker-compose up -d
該命令將以 detached 模式自動啟動 Docker Compose 配置中定義的所有容器。你可以通過 docker ps
來觀察上述的容器是否正常啟動了,也可以通過訪問 http://localhost:8081/ 來檢視 Flink 是否執行正常。
注意:
- 本教程接下來用到的容器相關的命令都需要在
docker-compose.yml
所在目錄下執行。 - 為了簡化整個教程,本教程需要的 jar 包都已經被打包進 SQL-Client 容器中了,映象的構建指令碼可以在 GitHub [2] 上找到。
如果你想要在自己的 Flink 環境執行本教程,需要下載下面列出的包並且把它們放在 Flink 所在目錄的 lib 目錄下,即 FLINK_HOME/lib/
。
- flink-sql-connector-mysql-cdc-2.1.0.jar
- flink-shaded-hadoop-2-uber-2.7.5-10.0.jar
- iceberg-flink-1.13-runtime-0.13.0-SNAPSHOT.jar
截止目前支援 Flink 1.13 的 iceberg-flink-runtime
jar 包還沒有釋出,所以我們在這裡提供了一個支援 Flink 1.13 的 iceberg-flink-runtime
jar 包,這個 jar 包是基於 Iceberg 的 master 分支打包的。
當 Iceberg 0.13.0 版本釋出後,你也可以在 apache official repository [3] 下載到支援 Flink 1.13 的 iceberg-flink-runtime
jar 包。
1.2 準備資料
進入 MySQL 容器中:
docker-compose exec mysql mysql -uroot -p123456
- 建立資料和表,並填充資料。
建立兩個不同的資料庫,並在每個資料庫中建立兩個表,作為 user
表分庫分表下拆分出的表。
CREATE DATABASE db_1;
USE db_1;
CREATE TABLE user_1 (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL DEFAULT 'flink',
address VARCHAR(1024),
phone_number VARCHAR(512),
email VARCHAR(255)
);
INSERT INTO user_1 VALUES (110,"user_110","Shanghai","123567891234","user_110@foo.com");
CREATE TABLE user_2 (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL DEFAULT 'flink',
address VARCHAR(1024),
phone_number VARCHAR(512),
email VARCHAR(255)
);
INSERT INTO user_2 VALUES (120,"user_120","Shanghai","123567891234","user_120@foo.com");
CREATE DATABASE db_2;
USE db_2;
CREATE TABLE user_1 (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL DEFAULT 'flink',
address VARCHAR(1024),
phone_number VARCHAR(512),
email VARCHAR(255)
);
INSERT INTO user_1 VALUES (110,"user_110","Shanghai","123567891234", NULL);
CREATE TABLE user_2 (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL DEFAULT 'flink',
address VARCHAR(1024),
phone_number VARCHAR(512),
email VARCHAR(255)
);
INSERT INTO user_2 VALUES (220,"user_220","Shanghai","123567891234","user_220@foo.com");
二、在 Flink SQL CLI 中使用 Flink DDL 建立表
首先,使用如下的命令進入 Flink SQL CLI 容器中:
docker-compose exec sql-client ./sql-client
我們可以看到如下介面:
然後,進行如下步驟:
- 開啟 checkpoint
Checkpoint 預設是不開啟的,我們需要開啟 Checkpoint 來讓 Iceberg 可以提交事務。
並且,mysql-cdc 在 binlog 讀取階段開始前,需要等待一個完整的 checkpoint 來避免 binlog 記錄亂序的情況。
-- Flink SQL
-- 每隔 3 秒做一次 checkpoint
Flink SQL> SET execution.checkpointing.interval = 3s;
- 建立 MySQL 分庫分表 source 表
建立 source 表 user_source
來捕獲MySQL中所有 user
表的資料,在表的配置項 database-name
, table-name
使用正規表示式來匹配這些表。
並且,user_source
表也定義了 metadata 列來區分資料是來自哪個資料庫和表。
-- Flink SQL
Flink SQL> CREATE TABLE user_source (
database_name STRING METADATA VIRTUAL,
table_name STRING METADATA VIRTUAL,
`id` DECIMAL(20, 0) NOT NULL,
name STRING,
address STRING,
phone_number STRING,
email STRING,
PRIMARY KEY (`id`) NOT ENFORCED
) WITH (
'connector' = 'mysql-cdc',
'hostname' = 'mysql',
'port' = '3306',
'username' = 'root',
'password' = '123456',
'database-name' = 'db_[0-9]+',
'table-name' = 'user_[0-9]+'
);
- 建立 Iceberg sink 表
建立 sink 表 all_users_sink
,用來將資料載入至 Iceberg 中。
在這個 sink 表,考慮到不同的 MySQL 資料庫表的 id
欄位的值可能相同,我們定義了複合主鍵 (database_name
, table_name
, id
)。
-- Flink SQL
Flink SQL> CREATE TABLE all_users_sink (
database_name STRING,
table_name STRING,
`id` DECIMAL(20, 0) NOT NULL,
name STRING,
address STRING,
phone_number STRING,
email STRING,
PRIMARY KEY (database_name, table_name, `id`) NOT ENFORCED
) WITH (
'connector'='iceberg',
'catalog-name'='iceberg_catalog',
'catalog-type'='hadoop',
'warehouse'='file:///tmp/iceberg/warehouse',
'format-version'='2'
);
三、流式寫入 Iceberg
使用下面的 Flink SQL 語句將資料從 MySQL 寫入 Iceberg 中:
-- Flink SQL Flink SQL> INSERT INTO all_users_sink select * from user_source;
上述命令將會啟動一個流式作業,源源不斷將 MySQL 資料庫中的全量和增量資料同步到 Iceberg 中。
在 Flink UI [4] 上可以看到這個執行的作業:
然後我們就可以使用如下的命令看到 Iceberg 中的寫入的檔案:
docker-compose exec sql-client tree /tmp/iceberg/warehouse/default_database/
如下所示:
在你的執行環境中,實際的檔案可能與上面的截圖不相同,但是整體的目錄結構應該相似。
使用下面的 Flink SQL 語句查詢表
all_users_sink
中的資料:-- Flink SQL Flink SQL> SELECT * FROM all_users_sink;
在 Flink SQL CLI 中我們可以看到如下查詢結果:
修改 MySQL 中表的資料,Iceberg 中的表 all_users_sink
中的資料也將實時更新:
(3.1) 在 db_1.user_1
表中插入新的一行
--- db_1
INSERT INTO db_1.user_1 VALUES (111,"user_111","Shanghai","123567891234","user_111@foo.com");
(3.2) 更新 db_1.user_2
表的資料
--- db_1
UPDATE db_1.user_2 SET address='Beijing' WHERE id=120;
(3.3) 在 db_2.user_2
表中刪除一行
--- db_2
DELETE FROM db_2.user_2 WHERE id=220;
每執行一步,我們就可以在 Flink Client CLI 中使用 SELECT * FROM all_users_sink
查詢表 all_users_sink
來看到資料的變化。
最後的查詢結果如下所示:
從 Iceberg 的最新結果中可以看到新增了(db_1, user_1, 111)
的記錄,(db_1, user_2, 120)
的地址更新成了 Beijing
,且(db_2, user_2, 220)
的記錄被刪除了,與我們在 MySQL 做的資料更新完全一致。
四、環境清理
本教程結束後,在 docker-compose.yml
檔案所在的目錄下執行如下命令停止所有容器:
docker-compose down
五、總結
在本文中,我們展示瞭如何使用 Flink CDC 同步 MySQL 分庫分表的資料,快速構建 Icberg 實時資料湖。使用者也可以同步其他資料庫(Postgres/Oracle)的資料到 Hudi 等資料湖中。最後希望通過本文,能夠幫助讀者快速上手 Flink CDC 。
更多 Flink CDC 相關技術問題,可掃碼加入社群釘釘交流群~
註釋:
[1] https://iceberg.apache.org/
[2] https://github.com/luoyuxia/f...
[3] https://repo.maven.apache.org...
Flink Forward Asia 2021
2022 年 1 月 8-9 日,FFA 2021 重磅開啟,全球 40+ 多行業一線廠商,80+ 乾貨議題,帶來專屬於開發者的技術盛宴。
大會官網:
https://flink-forward.org.cn
大會線上觀看地址 (記得預約哦):
https://developer.aliyun.com/...
更多 Flink 相關技術問題,可掃碼加入社群釘釘交流群
第一時間獲取最新技術文章和社群動態,請關注公眾號~