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();
相关推荐
bubble小拾2 小时前
ElasticSearch高级功能详解与读写性能调优
大数据·elasticsearch·搜索引擎
ZOHO项目管理软件2 小时前
EDM平台大比拼 用户体验与营销效果双重测评
大数据
HyperAI超神经3 小时前
Meta 首个多模态大模型一键启动!首个多针刺绣数据集上线,含超 30k 张图片
大数据·人工智能·深度学习·机器学习·语言模型·大模型·数据集
Hello.Reader5 小时前
TopK算法在大数据重复数据分析中的应用与挑战
大数据·算法·数据分析
数据龙傲天5 小时前
1688商品API接口:电商数据自动化的新引擎
java·大数据·sql·mysql
Elastic 中国社区官方博客5 小时前
Elasticsearch:使用 LLM 实现传统搜索自动化
大数据·人工智能·elasticsearch·搜索引擎·ai·自动化·全文检索
Jason不在家7 小时前
Flink 本地 idea 调试开启 WebUI
大数据·flink·intellij-idea
Elastic 中国社区官方博客8 小时前
使用 Vertex AI Gemini 模型和 Elasticsearch Playground 快速创建 RAG 应用程序
大数据·人工智能·elasticsearch·搜索引擎·全文检索
CHICX12299 小时前
【Hadoop】改一下core-site.xml和hdfs-site.xml配置就可以访问Web UI
xml·大数据·hadoop
权^10 小时前
MySQL--聚合查询、联合查询、子查询、合并查询(上万字超详解!!!)
大数据·数据库·学习·mysql