Flink CDC 与Hudi整合
介绍
之前写过Flink CDC sink 到 Iceberg中,本篇主要实践如何CDC到hudi中.
什么是hudi?
Hudi is a rich platform to build streaming data lakes with incremental data pipelines
on a self-managing database layer, while being optimized for lake engines and regular batch processing.

hudi 主要解决什么问题?
- HDFS的可伸缩性限制
- 需要在Hadoop中更快地呈现数据
- 没有直接支持对现有数据的更新和删除
- 快速的ETL和建模
- 要检索所有更新的记录,无论这些更新是添加到最近日期分区的新记录还是对旧数据的更新,Hudi都允许用户使用最后一个检查点时间戳。此过程不用执行扫描整个源表的查询
hudi的特性:
- Upserts, Deletes with fast, pluggable indexing.
- Incremental queries, Record level change streams
- Transactions, Rollbacks, Concurrency Control.
- SQL Read/Writes from Spark, Presto, Trino, Hive & more
- Automatic file sizing, data clustering, compactions, cleaning.
- Streaming ingestion, Built-in CDC sources & tools.
- Built-in metadata tracking for scalable storage access.
- Backwards compatible schema evolution and enforcement.
Flink CDC 与 Hudi整合
版本
Flink: 1.13.1
Hudi: 0.10.1
环境搭建
使用本地环境, hadoop 使用之前虚拟机安装的环境
MySQL Docker 安装个镜像,主要用于模拟数据变更,产生binlog数据
dockerpull mysql:latest
docker run -itd--name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql
进入容器,可以使用mysql连接验证:
dockerexec -it 07e946b1fa9a /bin/bash
mysql -uroot -p123456
创建MySQL表:
createtable users
(
id bigint auto_increment primary key,
name varchar(20) null,
birthday timestamp defaultCURRENT_TIMESTAMP not null,
ts timestamp defaultCURRENT_TIMESTAMP not null,
sex int
);
整合代码实践
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chaplinthink</groupId>
<artifactId>flink-hudi</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.2.1</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-core</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>1.13.1</version>
</dependency> <!-- <dependency>--> <!-- <groupId>org.apache.flink</groupId>--> <!-- <artifactId>flink-jdbc_2.12</artifactId>--> <!-- <version>1.10.3</version>--> <!-- </dependency>-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-jdbc_2.11</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-api-java-bridge_2.11</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-common</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner_2.11</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.11</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.11</artifactId>
<version>1.13.1</version>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-runtime-web_2.11</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>com.ververica</groupId>
<!-- add the dependency matching your database -->
<artifactId>flink-sql-connector-mysql-cdc</artifactId>
<!-- The dependency is available only for stable releases, SNAPSHOT dependency need build by yourself. -->
<version>2.2.0</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.alibaba.ververica</groupId>-->
<!-- <artifactId>flink-connector-mysql-cdc</artifactId>-->
<!-- <version>1.2.0</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.apache.hudi</groupId>
<artifactId>hudi-flink-bundle_2.11</artifactId>
<version>0.10.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
</project>
使用FlinkSQL 创建MySQL数据源表、Hudi目标表,通过
INSERT INTO hudi_users2 SELECT *, DATE_FORMAT(birthday, 'yyyyMMdd') FROM mysql_users 将数据写入hudi
核心代码:
final EnvironmentSettings fsSettings = EnvironmentSettings.newInstance()
.useBlinkPlanner()
.inStreamingMode()
.build();
final StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();
environment.setParallelism(1);
environment.enableCheckpointing(3000);
final StreamTableEnvironment tableEnvironment = StreamTableEnvironment.create(environment, fsSettings);
tableEnvironment.getConfig().setSqlDialect(SqlDialect.DEFAULT);
// 数据源表
String sourceDDL = "CREATE TABLE mysql_users (\n" +
" id BIGINT PRIMARY KEY NOT ENFORCED ,\n" +
" name STRING,\n" +
" birthday TIMESTAMP(3),\n" +
" ts TIMESTAMP(3)\n" +
") WITH (\n" +
" 'connector' = 'mysql-cdc',\n" +
" 'hostname' = '192.168.70.3',\n" +
" 'port' = '3306', " +
" 'username' = 'aa',\n" +
" 'password' = 'aa', " +
" 'server-time-zone' = 'Asia/Shanghai'," +
" 'database-name' = 'test',\n" +
" 'table-name' = 'users'\n" +
" )";
/**
* 触发器策略是在完成五次提交后执行压缩
*/
// 输出目标表
String sinkDDL = "CREATE TABLE hudi_users2\n" +
"(\n" +
" id BIGINT PRIMARY KEY NOT ENFORCED,\n" +
" name STRING,\n" +
" birthday TIMESTAMP(3),\n" +
" ts TIMESTAMP(3),\n" +
" `partition` VARCHAR(20)\n" +
") PARTITIONED BY (`partition`) WITH (\n" +
" 'connector' = 'hudi',\n" +
" 'table.type' = 'MERGE_ON_READ',\n" +
" 'path' = 'hdfs://ip:8020/hudi/hudi_users2'\n " +
")";
String transformSQL = "INSERT INTO hudi_users2 SELECT *, DATE_FORMAT(birthday, 'yyyyMMdd') FROM mysql_users\n";
tableEnvironment.executeSql(sourceDDL);
tableEnvironment.executeSql(sinkDDL);
tableEnvironment.executeSql(transformSQL);
environment.execute("mysql-to-hudi");
本地启动Flink程序
然后进行MySQL DML 操作
insertinto users (name) values ('hello');
insertinto users (name) values ('world');
insertinto users (name) values ('iceberg');
insertinto users (name) values ('hudi');
update users set name = 'hello spark' where id = 4;
delete from users where id = 5;
查看HDFS上hudi数据路径:

Hudi 默认情况下,MERGE_ON_READ表的压缩是启用的, 触发器策略是在完成五次提交后执行压缩. 在MySQL执行insert、update、delete等操作后,就可以用hive/spark-sql/presto进行查询。
如果没有生成parquet文件,我们建的parquet表是查询不出数据的。
五次提交后可以看到数据文件:

关掉Flink CDC程序, 单独写个FlinkSQL程序读取HDFS 上hudi数据:
public static void main(String[] args) throwsException {
final EnvironmentSettings fsSettings =EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
final StreamExecutionEnvironmentenvironment = StreamExecutionEnvironment.getExecutionEnvironment();
environment.setParallelism(1);
final StreamTableEnvironmenttableEnvironment = StreamTableEnvironment.create(environment, fsSettings);
tableEnvironment.getConfig().setSqlDialect(SqlDialect.DEFAULT);
String sourceDDL = "CREATE TABLEhudi_users2\n" +
"(\n" +
" id BIGINT PRIMARY KEY NOT ENFORCED,\n"+
" name STRING,\n" +
" birthday TIMESTAMP(3),\n" +
" ts TIMESTAMP(3),\n" +
" `partition` VARCHAR(20)\n" +
") PARTITIONED BY(`partition`) WITH (\n" +
" 'connector' = 'hudi',\n" +
" 'table.type' = 'MERGE_ON_READ',\n" +
" 'path' ='hdfs://ip:8020/hudi/hudi_users2',\n" +
" 'read.streaming.enabled' = 'true',\n"+
" 'read.streaming.check-interval' = '1'\n" +
")";
tableEnvironment.executeSql(sourceDDL);
TableResult result2 =tableEnvironment.executeSql("select * from hudi_users2");
result2.print();
environment.execute("read_hudi");
}
FlinkSQL读取到打印的数据:

与MySQL 数据库表数据比对可以看到数据是一致的:

至此flink + hudi 湖仓一体化方案的原型就构建完成了.
总结
本篇主要讲解Flink CDC与hudi整合实践, 探索新的湖仓一体架构, 业内37手游的湖仓一体架构也可供参考如下:

对频繁增加表字段的痛点需求,同步下游系统的时候希望能够自动加入这个字段,目前还没有完美的解决方案,Flink CDC社区后续看是否提供 Schema Evolution 的支持.
目前MySQL新增字段,是需要修改Flink程序,然后重启.
参考:
- https://hudi.apache.org/cn/
- https://cloud.tencent.com/developer/article/1884134
- https://developer.aliyun.com/article/791526
Flink CDC 与Hudi整合的更多相关文章
- 重磅!解锁Apache Flink读写Apache Hudi新姿势
感谢阿里云 Blink 团队Danny Chan的投稿及完善Flink与Hudi集成工作. 1. 背景 Apache Hudi 是目前最流行的数据湖解决方案之一,Data Lake Analytics ...
- Fllin(七)【Flink CDC实践】
目录 FlinkCDC 1.简介 2.依赖 3.flink stream api 4.flink sql 5.自定义反序列化器 6.打包测试 FlinkCDC 1.简介 CDC是Change Data ...
- 使用Apache Flink 和 Apache Hudi 创建低延迟数据湖管道
近年来出现了从单体架构向微服务架构的转变.微服务架构使应用程序更容易扩展和更快地开发,支持创新并加快新功能上线时间.但是这种方法会导致数据存在于不同的孤岛中,这使得执行分析变得困难.为了获得更深入和更 ...
- 基于Apache Hudi 的CDC数据入湖
作者:李少锋 文章目录: 一.CDC背景介绍 二.CDC数据入湖 三.Hudi核心设计 四.Hudi未来规划 1. CDC背景介绍 首先我们介绍什么是CDC?CDC的全称是Change data Ca ...
- 基于Apache Hudi + Flink的亿级数据入湖实践
本次分享分为5个部分介绍Apache Hudi的应用与实践 实时数据落地需求演进 基于Spark+Hudi的实时数据落地应用实践 基于Flink自定义实时数据落地实践 基于Flink+Hudi的应用实 ...
- Apache Hudi与Apache Flink集成
感谢王祥虎@wangxianghu 投稿 Apache Hudi是由Uber开发并开源的数据湖框架,它于2019年1月进入Apache孵化器孵化,次年5月份顺利毕业晋升为Apache顶级项目.是当前最 ...
- Apache Hudi核心概念一网打尽
1. 场景 https://hudi.apache.org/docs/use_cases.html 近实时写入 减少碎片化工具的使用 CDC 增量导入 RDBMS 数据 限制小文件的大小和数量 近实时 ...
- Apache Hudi C位!云计算一哥AWS EMR 2020年度回顾
1. 概述 成千上万的客户在Amazon EMR上使用Apache Spark,Apache Hive,Apache HBase,Apache Flink,Apache Hudi和Presto运行大规 ...
- 对话Apache Hudi VP, 洞悉数据湖的过去现在和未来
Apache Hudi是一个开源数据湖管理平台,用于简化增量数据处理和数据管道开发,该平台可以有效地管理业务需求,例如数据生命周期,并提高数据质量.Hudi的一些常见用例是记录级的插入.更新和删除.简 ...
- 触宝科技基于Apache Hudi的流批一体架构实践
1. 前言 当前公司的大数据实时链路如下图,数据源是MySQL数据库,然后通过Binlog Query的方式消费或者直接客户端采集到Kafka,最终通过基于Spark/Flink实现的批流一体计算引擎 ...
随机推荐
- 为何AI更懂你:向量搜索,了解一下!
现在,你有没有发现自己越来越多地依赖推荐系统,有时候自己搜到的结果好像还没有AI推荐的精准. 那估计有人好奇了,推荐系统怎么这么"聪明"的呢?答案就是:"向量搜索&quo ...
- 【工具分享】红队重点资产指纹识别 -- P1finger -0.02(最新版本)
工具介绍: P1finger 红队行动下的重点资产指纹识别工具.P1finger 是一个重点资产指纹识别的工具,旨在通过HTTP请求特征来识别目标系统.其主要特点包括: 语言和实现: 语言:使用Go语 ...
- 【图文教程】云服务器上,Linux安装VSFTPD组件及遇到的问题
服务器做迁移,从AXX云迁移到Txx云上,迁移的话,需要把图片服务器也迁移过去.之前使用的是VSFTPD这次也还用这个吧.这里就记录下FTP服务器安装及遇到的问题. 1:安装VSFTP组件 使用yum ...
- 爬虫案例1-爬取图片的三种方式之一:DrissionPage篇(3)
@ 目录 前言 DrissionPage介绍 实战 共勉 博客 前言 继requests篇和selenium篇,本文是爬取图片的最后一个案例,利用了python第三方库DrissionPage来自动化 ...
- CSS & JS Effect – sticky horizontal scrollbar
需求 这个是 Google Ads 里的 table. 那个 horizontal scrollbar 可以 sticky bottom. 我们知道 scrollbar 是游览器原生的,我们能做的 s ...
- CSS – Variables
参考: Youtube – CSS Variables - CSS vs Sass - variables inside media queries Why we prefer CSS Custom ...
- C++中对象的延迟构造
本文并不讨论"延迟初始化"或者是"懒加载的单例"那样的东西,本文要讨论的是分配某一类型所需的空间后不对类型进行构造(即对象的lifetime没有开始),更通俗点 ...
- 网络安全&密码学—python中的各种加密算法
网络安全&密码学-python中的各种加密算法 一.简介 数据加密是一种保护数据安全的技术,通过将数据(明文)转换为不易被未经授权的人理解的形式(密文),以防止数据泄露.篡改或滥用.加密后的数 ...
- idea运行java项目main方法报build failure错误的解决方法
当在使用 IntelliJ IDEA 运行 Java 项目的 main 方法时遇到 "Build Failure" 错误,这通常意味着在项目的构建过程中遇到了问题.解决这类问题通常 ...
- 2024年常用的Python可视化框架及开源项目
以下是 2024 年一些较为流行的 Python 可视化框架及开源项目: Matplotlib 框架声明:是 Python 中最基础.最广泛使用的可视化库之一,用于创建各种静态.动态和交互式图表. 官 ...