Flink CDC 系列 - 同步 MySQL 分庫分表,構建 Iceberg 實時資料湖

ApacheFlink發表於2021-12-28

作者:羅宇俠

本篇教程將展示如何使用 Flink CDC 構建實時資料湖,並處理分庫分表合併同步的場景。
Flink-CDC 專案地址:

https://github.com/ververica/...

Flink 中文學習網站
https://flink-learning.org.cn

在 OLTP 系統中,為了解決單表資料量大的問題,通常採用分庫分表的方式將單個大表進行拆分以提高系統的吞吐量。

但是為了方便資料分析,通常需要將分庫分表拆分出的表在同步到資料倉儲、資料湖時,再合併成一個大表。

這篇教程將展示如何使用 Flink CDC 構建實時資料湖來應對這種場景,本教程的演示基於 Docker,只涉及 SQL,無需一行 Java/Scala 程式碼,也無需安裝 IDE,你可以很方便地在自己的電腦上完成本教程的全部內容。

接下來將以資料從 MySQL 同步到 Iceberg [1] 為例展示整個流程,架構圖如下所示:

real-time-data-lake-tutorial

一、準備階段

準備一臺已經安裝了 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 是否執行正常。

flink-ui

注意:

  1. 本教程接下來用到的容器相關的命令都需要在 docker-compose.yml 所在目錄下執行。
  2. 為了簡化整個教程,本教程需要的 jar 包都已經被打包進 SQL-Client 容器中了,映象的構建指令碼可以在 GitHub [2] 上找到。

如果你想要在自己的 Flink 環境執行本教程,需要下載下面列出的包並且把它們放在 Flink 所在目錄的 lib 目錄下,即 FLINK_HOME/lib/

截止目前支援 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 準備資料

  1. 進入 MySQL 容器中:

    docker-compose exec mysql mysql -uroot -p123456
  1. 建立資料和表,並填充資料。

建立兩個不同的資料庫,並在每個資料庫中建立兩個表,作為 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

我們可以看到如下介面:

img

然後,進行如下步驟:

  1. 開啟 checkpoint

Checkpoint 預設是不開啟的,我們需要開啟 Checkpoint 來讓 Iceberg 可以提交事務。
並且,mysql-cdc 在 binlog 讀取階段開始前,需要等待一個完整的 checkpoint 來避免 binlog 記錄亂序的情況。

-- Flink SQL
-- 每隔 3 秒做一次 checkpoint                 
Flink SQL> SET execution.checkpointing.interval = 3s;
  1. 建立 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]+'
  );
  1. 建立 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

  1. 使用下面的 Flink SQL 語句將資料從 MySQL 寫入 Iceberg 中:

    -- Flink SQL
    Flink SQL> INSERT INTO all_users_sink select * from user_source;

上述命令將會啟動一個流式作業,源源不斷將 MySQL 資料庫中的全量和增量資料同步到 Iceberg 中。
Flink UI [4] 上可以看到這個執行的作業:

flink-cdc-iceberg-running-job

然後我們就可以使用如下的命令看到 Iceberg 中的寫入的檔案:

docker-compose exec sql-client tree /tmp/iceberg/warehouse/default_database/

如下所示:

files-in-iceberg

在你的執行環境中,實際的檔案可能與上面的截圖不相同,但是整體的目錄結構應該相似。

  1. 使用下面的 Flink SQL 語句查詢表 all_users_sink 中的資料:

    -- Flink SQL
    Flink SQL> SELECT * FROM all_users_sink;

在 Flink SQL CLI 中我們可以看到如下查詢結果:

data_in_iceberg

修改 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 來看到資料的變化。

最後的查詢結果如下所示:

final-data-in-iceberg

從 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 相關技術問題,可掃碼加入社群釘釘交流群~

img

註釋:

[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/...

img

更多 Flink 相關技術問題,可掃碼加入社群釘釘交流群
第一時間獲取最新技術文章和社群動態,請關注公眾號~

image.png

相關文章