- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
Flink在1.11版本中新增了CDC的特性,简称 改变数据捕获。名称来看有点乱,我们先从之前的数据架构来看CDC的内容。
以上是之前的mysql binlog日志处理流程,例如canal监听binlog把日志写入到kafka中。而Apache Flink实时消费Kakfa的数据实现mysql数据的同步或其他内容等。拆分来说整体上可以分为以下几个阶段。
1、 mysql开启binlog;
2、 canal同步binlog数据写入到kafka;
3、 flink读取kakfa中的binlog数据进行相关的业务处理;
整体的处理链路较长,需要用到的组件也比较多。Apache Flink CDC可以直接从数据库获取到binlog供下游进行业务计算分析。简单来说链路会变成这样
也就是说数据不再通过canal与kafka进行同步,而flink直接进行处理mysql的数据。节省了canal与kafka的过程。
Flink 1.11中实现了mysql-cdc与postgre-CDC,也就是说在Flink 1.11中我们可以直接通过Flink来直接消费mysql,postgresql的数据进行业务的处理。
使用场景:
1、 数据库数据的增量同步;
2、 数据库表之上的物理化视图;
3、 维表join;
4、 其他业务处理;
MySQL必须开启binlog
MySQL表必须有主键
mysql> show variables like '%log_bin%';
+---------------------------------+---------------------------------------------+
| Variable_name | Value |
+---------------------------------+---------------------------------------------+
| log_bin | ON |
| log_bin_basename | /home/mysql/data/3306/10-31-1-122-bin |
| log_bin_index | /home/mysql/data/3306/10-31-1-122-bin.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+---------------------------------------------+
6 rows in set (0.01 sec)
MySQL代码:
create databases cdc_test;
create table test1(id int primary key,name varchar(50),create_datetime timestamp(0));
insert into test1(id,name,create_datetime) values (1,'abc',current_timestamp());
insert into test1(id,name,create_datetime) values (2,'def',current_timestamp());
insert into test1(id,name,create_datetime) values (3,'ghi',current_timestamp());
update test1 set name = 'aaa' where id = 1;
delete from test1 where id = 1;
create table test2(id int primary key,name varchar(50),create_datetime timestamp(0));
delete from test1 where id = 1;
insert into test2(id,name,create_datetime) values (1,'abc',current_timestamp());
drop table test2;
pom文件配置如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.ververica</groupId>
<artifactId>flink-connector-mysql-cdc</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.12</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.12</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.12</artifactId>
<version>1.12.0</version>
<type>test-jar</type>
</dependency>
</dependencies>
CdcDwdDeserializationSchema
package com.zqs.study.flink.cdc;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.ververica.cdc.debezium.DebeziumDeserializationSchema;
import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.util.Collector;
import org.apache.kafka.connect.data.Field;
import org.apache.kafka.connect.data.Schema;
import org.apache.kafka.connect.data.Struct;
import org.apache.kafka.connect.source.SourceRecord;
import java.util.List;
public class CdcDwdDeserializationSchema implements DebeziumDeserializationSchema<JSONObject> {
private static final long serialVersionUID = -3168848963265670603L;
public CdcDwdDeserializationSchema() {
}
@Override
public void deserialize(SourceRecord record, Collector<JSONObject> out) {
Struct dataRecord = (Struct) record.value();
Struct afterStruct = dataRecord.getStruct("after");
Struct beforeStruct = dataRecord.getStruct("before");
/*
todo 1,同时存在 beforeStruct 跟 afterStruct数据的话,就代表是update的数据
2,只存在 beforeStruct 就是delete数据
3,只存在 afterStruct数据 就是insert数据
*/
JSONObject logJson = new JSONObject();
String canal_type = "";
List<Field> fieldsList = null;
if (afterStruct != null && beforeStruct != null) {
System.out.println("这是修改数据");
canal_type = "update";
fieldsList = afterStruct.schema().fields();
//todo 字段与值
for (Field field : fieldsList) {
String fieldName = field.name();
Object fieldValue = afterStruct.get(fieldName);
// System.out.println("*****fieldName=" + fieldName+",fieldValue="+fieldValue);
logJson.put(fieldName, fieldValue);
}
} else if (afterStruct != null) {
System.out.println("这是新增数据");
canal_type = "insert";
fieldsList = afterStruct.schema().fields();
//todo 字段与值
for (Field field : fieldsList) {
String fieldName = field.name();
Object fieldValue = afterStruct.get(fieldName);
// System.out.println("*****fieldName=" + fieldName+",fieldValue="+fieldValue);
logJson.put(fieldName, fieldValue);
}
} else if (beforeStruct != null) {
System.out.println("这是删除数据");
canal_type = "detele";
fieldsList = beforeStruct.schema().fields();
//todo 字段与值
for (Field field : fieldsList) {
String fieldName = field.name();
Object fieldValue = beforeStruct.get(fieldName);
// System.out.println("*****fieldName=" + fieldName+",fieldValue="+fieldValue);
logJson.put(fieldName, fieldValue);
}
} else {
System.out.println("一脸蒙蔽了");
}
//todo 拿到databases table信息
Struct source = dataRecord.getStruct("source");
Object db = source.get("db");
Object table = source.get("table");
Object ts_ms = source.get("ts_ms");
logJson.put("canal_database", db);
logJson.put("canal_database", table);
logJson.put("canal_ts", ts_ms);
logJson.put("canal_type", canal_type);
//todo 拿到topic
String topic = record.topic();
System.out.println("topic = " + topic);
//todo 主键字段
Struct pk = (Struct) record.key();
List<Field> pkFieldList = pk.schema().fields();
int partitionerNum = 0;
for (Field field : pkFieldList) {
Object pkValue = pk.get(field.name());
partitionerNum += pkValue.hashCode();
}
int hash = Math.abs(partitionerNum) % 3;
logJson.put("pk_hashcode", hash);
out.collect(logJson);
}
@Override
public TypeInformation<JSONObject> getProducedType() {
return BasicTypeInfo.of(JSONObject.class);
}
}
FlinkCDCSQLTest
package com.zqs.study.flink.cdc;
/**
* @remark Flink CDC 测试
*/
import com.alibaba.fastjson.JSONObject;
import com.alibaba.ververica.cdc.connectors.mysql.MySQLSource;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
public class FlinkCDCSQLTest {
public static void main(String[] args) {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
SourceFunction<JSONObject> sourceFunction = MySQLSource.<JSONObject>builder()
.hostname("10.31.1.122")
.port(3306)
.databaseList("cdc_test") // monitor all tables under inventory database
.username("root")
//.password("abc123")
.password("Abc123456!")
.deserializer(new CdcDwdDeserializationSchema()) // converts SourceRecord to String
.build();
DataStreamSource<JSONObject> stringDataStreamSource = env.addSource(sourceFunction);
stringDataStreamSource.print("===>");
try {
env.execute("测试mysql-cdc");
} catch (Exception e) {
e.printStackTrace();
}
}
}
如下截图所示,可以捕捉到DML语句,但是无法捕捉到DDL语句
我有一个要求,我想在新添加的表列上启用 CDC,但我无法禁用 CDC 并再次启用它。有什么办法可以实现这一目标吗? 我找到了一个解决方案,我可以将旧的 CDC 表值复制到临时表中,然后禁用 CDC,然
我已使用以下步骤启用 CDC: exec sys.sp_cdc_enable_db; exec sys.sp_cdc_enable_table @source_schema = N'dbo', @so
我正在尝试将AS400的IBM数据复制设置为MySQL。对于目标MySQL,现在是最新的MySQL 8.0.20社区服务器。我已经很好地测试了JDBC连接。但是,CDC实例永远不会启动(通过测试从8.
我正在寻找一种方法来捕获 mysql 插入、更新、删除操作并触发应用程序或脚本以使用这些修改后的数据以自定义格式插入到 mongodb 中。 最佳答案 也许这对于正在处理类似情况的人来说可能会派上用场
简介 Flink CDC Connector 是ApacheFlink的一组数据源连接器,使用变化数据捕获change data capture (CDC)从不同的数据库中提取变更数据。Fli
本文整理了Java中cdc.util.xml.XmlWriter类的一些代码示例,展示了XmlWriter类的具体用法。这些代码示例主要来源于Github/Stackoverflow/Maven等平台
是否可以将所有表的历史合并到一个表中? 我尝试使用 SQL server 2012 企业版 提供的 CDC 功能,但为此它创建了每个表的副本,这增加了数据库中表的数量。 是否也可以将发生DML的表名和
我正在 SQL Server 2012 企业版 (11.0.2100.60) 上启用更改数据捕获 (CDC) 。我可以使用以下 SQL 在数据库级别启用它,但无法在表级别启用。 Use Databas
我正在编写一个简单的虚拟串行端口设备来报告一个较旧的串行端口。至此,我能够枚举设备并发送/接收字符。 在从主机到设备的不同数量的批量传输之后,端点似乎放弃并停止传输数据。在 PC 端,我收到一个写入错
当我们谈论采购事件时,我们有一个简单的双写架构,我们可以写入数据库,然后将事件写入队列,如 Kafka。其他下游系统可以读取这些事件并相应地对它们采取行动/使用它们。 但是当尝试使数据库和事件同步时会
我读到,启用变更数据捕获显然会对数据库性能产生影响。这种性能损失是否仅影响启用了 CDC 的表,还是会影响数据库中的所有操作 就我而言,我使用的是 SSIS,并且有大量数据移入和移出临时数据库。我的系
我正在编写一个简单的虚拟串行端口设备来报告一个较旧的串行端口。至此,我能够枚举设备并发送/接收字符。 在从主机到设备的不同数量的批量传输之后,端点似乎放弃并停止传输数据。在 PC 端,我收到一个写入错
我尝试在WinCE环境中使用readfile函数从CDC设备读取数据。 BOOL WINAPI ReadFile( _In_ HANDLE hFile, _Out_
我将一些属于某些对象的符号绘制到设备上下文中,现在希望能够稍后测试鼠标光标是否位于此类符号之上。 为此,我的计划是首先创建一个 CDC 路径并使用它来创建一个 CRgn 区域对象。 pDC->Begi
我正在使用函数 CDC::Rectangle它使用逻辑坐标。但我想知道我绘制的区域有多大,因此我可以绘制区域宽度的 10% 的矩形。 如何从 CDC 获取坐标系的维度? 最佳答案 信不信由你,Wind
当我为我创建的报表控件打印 CDC 时,它看起来很小(在纸上小于 1 平方英寸)。如何让要打印的报告占据整个页面?或者换句话说,我怎样才能使整个报告显示在一个打印页面中。 CPrintDialog 打
我收到一个错误:在我的 myDC.DrawText 上调试断言失败?如果我删除那条线,图形就可以正常工作。 CPaintDC dc(this); CBitmap myBmp; CDC myDc; HA
在设备上下文中显示 IBitmapImage 的最佳方式是什么。我使用的是 Windows CE 6.0。 void CImaginingTestView::OnDraw(CDC* pDC) {
大家好,亲爱的专家和编码专家。 我不会从我是新手开始,对图像编程知之甚少,但不幸的是,这些都是事实:( 我正在尝试显示来自具有分辨率的位图指针 *ImageData 的图像1392x1032。我试图在
在我的一个项目中(VC++2010,MFC),我想用CDC::Ellipse画一个圆。我设置了两个点:第一个是圆心,第二个是我希望它在圆周上的一个点。 我将左上角和右下角的坐标传递给 CDC::Ell
我是一名优秀的程序员,十分优秀!