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();
相关推荐
hexionly18 分钟前
数据仓库·简介(一)
大数据·数据仓库
TDengine (老段)20 分钟前
TDengine 数学函数 TRUNCATE 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
TDengine (老段)25 分钟前
TDengine 数据函数 CORR 用户手册
大数据·数据库·物联网·时序数据库·tdengine·1024程序员节
现在,此刻5 小时前
flink学习与如何在springboot项目中使用flink
spring boot·学习·flink
隐语SecretFlow7 小时前
【隐语SecretFlow】由蚂蚁集团牵头制定的“隐私保护计算安全分级”IEEE国际标准已正式发布!
大数据·网络·安全
微三云、小叶11 小时前
裂变速度提升300%!279模式如何盘活一个私域商城
大数据·软件开发·商业模式·小程序商城·本地生活·商业思维
还是大剑师兰特12 小时前
Hadoop面试题及详细答案 110题 (106-110)-- Hadoop高级与实战
大数据·hadoop·分布式
努力成为一个程序猿.13 小时前
【问题排查】hadoop-shaded-guava依赖问题
大数据·hadoop·spark
达芬奇科普14 小时前
俄罗斯全面禁止汽油出口对俄、欧、中能源市场的多维影响分析
大数据·人工智能
wudl556615 小时前
Flink SQL连接Kafka及数据去重操作
sql·flink·kafka