flinkcdc 原理 + 实践

使用环境

Flink 1.14.2 + flink cdc 2.2.0

提示:flinkcdc 2.2版本之后才支持flink 1.14.*,

flinkcdc 2.2版本之前不支持 mysql低版本5.6的 cdc.

CDC1.*版本痛点

  1. 并发

  2. 为了保证一致性,一般通过全量 + 增量进行获取数据。 在全量阶段 会进行加锁操作。 通过对 binlog的起始位置和 表的 schema 锁住,保证在全量读取的时候阻止所有新的update。

  3. 全量 获取数据的时候不支持checkpoint, fail后需要重新读取。

CDC2.*版本介绍

flinkCDC2.* 优点 :

并发读取全量数据后无缝转换为单线程读取增量

通过高水位的方式来替代1.*版本的加锁操作来保证数据一致性。解决1.*版本痛点。

  • chunk切分:

    SourceEnumerator 将表按主键切分chunk.(没有主键可以指定) 分片数表示每个chunk用对应的task并行执 行。达到多并发。 task对 chunk 一对多的关系。 也就是说 一个sourceReader对应一个或多个chunk.

  • chunk分配:

    SourceReader , 每个SourceReader读取表中的一部分数据达到并行读取。

  • chunk读取: (单个Chunk中的数据一致性)

    SourceReader在读取表数据之前,也就是在读取这个chunk切片的时候,会记录当前的binlog位置信息为 低位点。 每个 chunk获取的低位点可能都不一样:比如: 10,30,20 的低位点。

    SourceReader将自身区间内的数据查询并放入buffer(快照读取,等待修正)

    SourceReader查询完成该chunk切片数据之后记录当前binlog位置信息为 高位点。 每个chunk获取的高位点可能也都不一样。比如: 80,70,90 的高位点。

    全量到增量衔接部分:每个chunk消费从低位点到高位点之间的binlog。对buffer数据进行修正。chunk最终的输出则是在对应的该高位点最新的数据。 但是目前只保证了单个chunk中的数据一致性。

  • chunk汇报:

    chunk读取完成之后,会对SourceEnumerator进行汇报,为的是后续的分发binlog chunk. 因为之前的数据针对每个chunk对应的高位点或者低位点获取到的数据范围不一致,为了保证后续继续处理增量的binlog,需要进行分发binlog.

  • chunk分配: (多个Chunk的一致性, 增量阶段)

    在后续需要处理的增量的binlog中,SourceEnumerator会通过下发binlog chunk给任意一个SourceReader进行单并发增量实现。因为增量通常都是数据库单并行操作的,多个task没有意义

    至此切换至增量阶段,会从已完成的全量的所有chunk中筛选出最小的binglog hw。 也就是70开始,当数据到来,判断数据所属chunk,其次,根据状态判断当前数据偏移量如果大于快照时的偏移量 70,那么进行下发,如果小于,则是该chunk的重复数据,直接丢弃。

    解决了全量时期单并发的痛点。并通过高水位方式保证了数据一致性,无缝切换全量 + 增量 阶段。

复制代码
final MySqlSource<String> build = MySqlSource.<String>builder()
        .hostname("127.0.0.1")
        .port(3307)
        .username("root")
        .password("123456")
        .databaseList("coocaa")
        .tableList("coocaa.Course")
        .deserializer(new StringDebeziumDeserializationSchema())
        .startupOptions(StartupOptions.initial())
        // initial:  表示先将之前数据同步,在根据现有的binlog持续更新。
        .build();

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
final DataStreamSource<String> dataStreamSource = env.fromSource(build, WatermarkStrategy.noWatermarks(),"mysql-cdc");
dataStreamSource.print();

env.execute();

// TODO: 初始化flink
StreamExecutionEnvironment streamEnv
        = StreamExecutionEnvironment.getExecutionEnvironment();
EnvironmentSettings envSettings = EnvironmentSettings.newInstance().inStreamingMode().build();
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(streamEnv, envSettings);
tableEnv.executeSql("" +
        "create table tablea(                                   \n" +
        "       database_name STRING METADATA VIRTUAL,          \n" +
        "       table_name STRING METADATA VIRTUAL,             \n" +
        "       cloumn1         String,                         \n" +
        "       cloumn2         String,                         \n" +
        "       cloumn3         String,                         \n" +
        "       cloumn4         String,                         \n" +
        "       cloumn5         String,                         \n" +
        "       cloumn6         String,                         \n" +
        "       cloumn7         String,                         \n" +
        "       cloumn8         int,                            \n" +
        "       cloumn9         int,                            \n" +
        "       cloumn10        int,                            \n" +
        "       cloumn11        String,                         \n" +
        "        PRIMARY KEY (cloumn1) NOT ENFORCED             \n" +
        " ) with (                                              \n" +
        "      'connector' = 'mysql-cdc',                       \n" +
        "      'hostname' = '***',                              \n" +
        "      'port' = '***',                                  \n" +
        "      'database-name' = '***',                         \n" +
        "      'username' = 'root',                             \n" +
        "      'password' = '123456',                           \n" +
        "      'table-name' = '***.*',                          \n" +   //可通过正则进行多库多表
        "      'scan.incremental.snapshot.enabled' = 'false',   \n" +
        "      'scan.startup.mode' = 'initial'                  \n" +       //initial  or latest-offset
        " )");

tableEnv.from("tablea").printSchema();
tableEnv.executeSql("select * from tablea").print();
相关推荐
令狐少侠20115 分钟前
elasticsearch之记录es7.17升级8.17 springboot2.7.0 程序改造坑
大数据·elasticsearch·jenkins
源码技术栈10 小时前
SaaS基于云计算、大数据的Java云HIS平台信息化系统源码
java·大数据·云计算·云his·his系统·云医院·区域his
Elastic 中国社区官方博客10 小时前
Elasticsearch 索引副本数
大数据·数据库·elasticsearch·搜索引擎·全文检索
Eternity......10 小时前
SparkSQL基本操作
大数据·spark
2685725910 小时前
Elasticsearch 初步认识
大数据·elasticsearch·搜索引擎·全文检索·es
python算法(魔法师版)10 小时前
网络编程入门(一)
大数据·网络·网络协议·计算机网络
caihuayuan512 小时前
生产模式下react项目报错minified react error #130的问题
java·大数据·spring boot·后端·课程设计
兔子坨坨13 小时前
详细了解HDFS
大数据·hadoop·hdfs·big data
夏旭泽13 小时前
系统架构-大数据架构设计
大数据·系统架构
Eternity......14 小时前
Spark,连接MySQL数据库,添加数据,读取数据
大数据·spark