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();
相关推荐
Dreams°1232 小时前
大数据 ETL + Flume 数据清洗 — 详细教程及实例(附常见问题及解决方案)
大数据·单元测试·可用性测试
静听山水2 小时前
Flink处理无界数据流
flink
sf_www2 小时前
Flink on YARN是如何确定TaskManager个数的
大数据·flink
静听山水2 小时前
Flink API 的层次结构
flink
武子康3 小时前
大数据-213 数据挖掘 机器学习理论 - KMeans Python 实现 距离计算函数 质心函数 聚类函数
大数据·人工智能·python·机器学习·数据挖掘·scikit-learn·kmeans
武子康3 小时前
大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels
大数据·人工智能·python·深度学习·算法·机器学习·数据挖掘
Aloudata4 小时前
NoETL自动化指标平台为数据分析提质增效,驱动业务决策
大数据·数据分析·指标平台·指标体系
2401_883041088 小时前
新锐品牌电商代运营公司都有哪些?
大数据·人工智能
青云交8 小时前
大数据新视界 -- 大数据大厂之 Impala 性能优化:融合机器学习的未来之路(上 (2-1))(11/30)
大数据·计算资源·应用案例·数据交互·impala 性能优化·机器学习融合·行业拓展
Json_1817901448011 小时前
An In-depth Look into the 1688 Product Details Data API Interface
大数据·json