当前位置:   article > 正文

flink教程-详解flink 1.11 中的CDC (Change Data Capture)_flink canal source

flink canal source

flink教程-详解flink 1.11 中的CDC (Change Data Capture)

大数据技术与应用实战

文章目录

 

CDC简介

CDC,Change Data Capture,变更数据获取的简称,使用CDC我们可以从数据库中获取已提交的更改并将这些更改发送到下游,供下游使用。这些变更可以包括INSERT,DELETE,UPDATE等,

用户可以在以下的场景下使用CDC:

  • 使用flink sql进行数据同步,可以将数据从一个数据同步到其他的地方,比如mysql、elasticsearch等。
  • 可以在源数据库上实时的物化一个聚合视图
  • 因为只是增量同步,所以可以实时的低延迟的同步数据
  • 使用EventTime join 一个temporal表以便可以获取准确的结果

flink 1.11 将这些changelog提取并转化为table apa和sql,目前支持两种格式:Debezium和Canal,这就意味着源表不仅仅是append操作,而且还有upsert、delete操作。

image

Canal

接下来我们使用canal为例简单介绍下CDC的使用

canal 格式:

  1. {
  2. "data": [
  3. {
  4. "id": "13",
  5. "username": "13",
  6. "password": "6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9",
  7. "name": "Canal Manager V2"
  8. }
  9. ],
  10. "old": [
  11. {
  12. "id": "13",
  13. "username": "13",
  14. "password": "6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9",
  15. "name": "Canal Manager"
  16. }
  17. ],
  18. "database": "canal_manager",
  19. "es": 1568972368000,
  20. "id": 11,
  21. "isDdl": false,
  22. "mysqlType": {...},
  23. "pkNames": [
  24. "id"
  25. ],
  26. "sql": "",
  27. "sqlType": {...},
  28. "table": "canal_user",
  29. "ts": 1568972369005,
  30. "type": "UPDATE"
  31. }

简单讲下几个核心的字段:

  • type : 描述操作的类型,包括‘UPDATE’, ‘INSERT’, ‘DELETE’。
  • data : 代表操作的数据。如果为’INSERT’,则表示行的内容;如果为’UPDATE’,则表示行的更新后的状态;如果为’DELETE’,则表示删除前的状态。
  • old :可选字段,如果存在,则表示更新之前的内容,如果不是update操作,则为 null。

完整的语义如下;

  1. private String destination; // 对应canal的实例或者MQ的topic
  2. private String groupId; // 对应mq的group id
  3. private String database; // 数据库或schema
  4. private String table; // 表名
  5. private List<String> pkNames;
  6. private Boolean isDdl;
  7. private String type; // 类型: INSERT UPDATE DELETE
  8. // binlog executeTime
  9. private Long es; // 执行耗时
  10. // dml build timeStamp
  11. private Long ts; // 同步时间
  12. private String sql; // 执行的sql, dml sql为空
  13. private List<Map<String, Object>> data; // 数据列表
  14. private List<Map<String, Object>> old; // 旧数据列表, 用于update, size和data的size一一对应
  1. -- 定义的字段和data 里面的数据想匹配
  2. CREATE TABLE my_table (
  3. id BIGINT,
  4. name STRING,
  5. description STRING,
  6. weight DECIMAL(10, 2)
  7. ) WITH (
  8. 'connector' = 'kafka',
  9. 'topic' = 'products_binlog',
  10. 'properties.bootstrap.servers' = 'localhost:9092',
  11. 'properties.group.id' = 'testGroup',
  12. 'canal-json.ignore-parse-errors'='true' -- 忽略解析错误,缺省值false
  13. );

CanalJson反序列化源码解析

canal 格式也是作为一种flink的格式,而且是source,所以也就是涉及到读取数据的时候进行反序列化,我们接下来就简单看看CanalJson的反序列化的实现。具体的实现类是CanalJsonDeserializationSchema。

我们看下这个最核心的反序列化方法:

  1. @Override
  2. public void deserialize(byte[] message, Collector<RowData> out) throws IOException {
  3. try {
  4. //使用json反序列化器将message反序列化成RowData
  5. RowData row = jsonDeserializer.deserialize(message);
  6. //获取type字段,用于下面的判断
  7. String type = row.getString(2).toString();
  8. if (OP_INSERT.equals(type)) {
  9. // 如果操作类型是insert,则data数组表示的是要插入的数据,则循环遍历data,然后添加一个标识INSERT,构造RowData对象,发送下游。
  10. ArrayData data = row.getArray(0);
  11. for (int i = 0; i < data.size(); i++) {
  12. RowData insert = data.getRow(i, fieldCount);
  13. insert.setRowKind(RowKind.INSERT);
  14. out.collect(insert);
  15. }
  16. } else if (OP_UPDATE.equals(type)) {
  17. // 如果是update操作,从data字段里获取更新后的数据、
  18. ArrayData data = row.getArray(0);
  19. // old字段获取更新之前的数据
  20. ArrayData old = row.getArray(1);
  21. for (int i = 0; i < data.size(); i++) {
  22. // the underlying JSON deserialization schema always produce GenericRowData.
  23. GenericRowData after = (GenericRowData) data.getRow(i, fieldCount);
  24. GenericRowData before = (GenericRowData) old.getRow(i, fieldCount);
  25. for (int f = 0; f < fieldCount; f++) {
  26. if (before.isNullAt(f)) {
  27. //如果old字段非空,则说明进行了数据的更新,如果old字段是null,则说明更新前后数据一样,这个时候把before的数据也设置成after的,也就是发送给下游的beforeafter数据一样。
  28. before.setField(f, after.getField(f));
  29. }
  30. }
  31. before.setRowKind(RowKind.UPDATE_BEFORE);
  32. after.setRowKind(RowKind.UPDATE_AFTER);
  33. //把更新前后的数据都发送下游
  34. out.collect(before);
  35. out.collect(after);
  36. }
  37. } else if (OP_DELETE.equals(type)) {
  38. // 如果是删除操作,data字段里包含将要被删除的数据,把这些数据组织起来发送给下游
  39. ArrayData data = row.getArray(0);
  40. for (int i = 0; i < data.size(); i++) {
  41. RowData insert = data.getRow(i, fieldCount);
  42. insert.setRowKind(RowKind.DELETE);
  43. out.collect(insert);
  44. }
  45. } else {
  46. if (!ignoreParseErrors) {
  47. throw new IOException(format(
  48. "Unknown \"type\" value \"%s\". The Canal JSON message is '%s'", type, new String(message)));
  49. }
  50. }
  51. } catch (Throwable t) {
  52. // a big try catch to protect the processing.
  53. if (!ignoreParseErrors) {
  54. throw new IOException(format(
  55. "Corrupt Canal JSON message '%s'.", new String(message)), t);
  56. }
  57. }
  58. }

参考资料:
[1].https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=147427289
[2].https://flink.apache.org/news/2020/07/06/release-1.11.0.html#table-apisql-support-for-change-data-capture-cdc

更多内容,欢迎关注我的公众号【大数据技术与应用实战】

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/723314
推荐阅读
相关标签
  

闽ICP备14008679号