项目背景
由于流量回放平台对录制环境和回放环境数据库一致性的要求很高,比如数据库的一致性误差在10分钟以内,数据库同步任务频繁等。mysqldump是mysql数据库提供的一个数据备份工具。mysqldump可以把mysql数据库导出成sql语句文件,并保存到磁盘上。mysqldump命令产生的.sql文件包含一系列SQL INSERT语句,可以用来进行数据恢复。进行数据导出和恢复的过程中耗费的时间较长远超过10分钟;且操作复杂,需要DBA协助。对于频繁的同步任务对DBA是不友好的。公司现有的数据库同步工具outter不支持全量数据同步;对应大数据量的数据同步,数据延迟比较大;对于大款表的同步频繁OOM。并且以上两种方式均不支持单租户级的数据同步。所以需要自己研发一款使用于流量回放平台的数据库同步工具,因此我们必须研发一款适用于流量回放平台的数据库同步工具。
需求分析
需要实现的目标
流量回放平台对数据库同步工具有以下要求:
- 源数据库(录制环境数据库)和目标数据(回放环境数据库)库的数据误差在开启录制时数据时间差在10分钟以内;
- 需要支持utf-8和ucs2(早期的大表使用的是此种字符集需要兼容)字符集;
- 可以自由选择需要同步的源数据库表和目标数据库表,且库名允许不一致;
- 由于不通环境的分库情况不同,还需要合并多个源库到一个目标库;
- 允许从源库同步单租户级别的数据至目标库;
- 支持源库数据增量同步至目标数据库;
- 支持单/多任务同步同一实例的多个数据库;
- 可以简单的操作就可以进行数据库任务同步,便于频繁数据库同步任务的执行。
技术方案的确定
CDC介绍
CDC的全称是Change Data Capture,是一种捕获增量数据的统称,目前主要应用在捕获数据库数据变更的技术,在数据同步、数据容灾、数据分发和数据集成等场景中。
目前业界比较流行的CDC技术解决方案主要分为以下两种:
基于查询的CDC
- 离线调度查询作业,通过JDBC的方式发起一次查询请求,服务端根据查询SQL进行解析优化执行并返回相应的结果集;
- 离线调度的方式天然的缺点是无法保证数据库的一致性和实时性,因为在查询的过程中数据可能发生了多次变化;
- 对源数据库有侵入性,对源数据库有一定的IO压力,所以在使用过程中需要充分考虑到源数据库压力,协调执行时间和执行并行数,或考虑从备库进行数据同步;
基于日志的CDC
- 实时消费日志,流处理。例如Mysql的日志文件binlog完整的记录了数据的变更,可以把binlog文件作为流得数据源;
- 保证了数据的一致性,因为日志文件完整的记录了数据的变更;
- 保证了数据的实时性,因为类似binlog日志的文件是可以流式消费的,提供的是实时的数据,只要处理得够快,就能保证源数据库和目标数据库的数据同步在秒级别
技术选型对比
对于常见的CDC开源数据库同步工具方案的对比,如下:
技术方案确定
通过以上的对比,可以看出得出以下结论:
- 在全量+增量同步的能力上是比较好的是Sqoop、Flink CDC、Debezium、Oracle Goldengate;
- 在1中支持断点续传的有Flink CDC、Debezium、Oracle Goldengate
- Oracle Goldengate主要是针对Oracle的;Flink CDC是基于Debezium开发的,并且进行了扩展增强,分布式架构,java语言开发,且社区比较活跃
所以我们决定基于Flink CDC来研发适用于流量回放平台的数据库同步工具(所有的表必须有主键)。
关于Flink CDC介绍,感兴趣的同学可以点击此处查看
功能实现
确定了技术方案后,我们就根据Flink CDC已有的功能上来实现自己的个性化定制,主要分为以下几个方面:
1.根据Flink CDC读取的数据进行解析,自定义反序列化方案
Flink CDC默认使用JsonDebeziumDeserializationSchema进行反序列化,把数据库的数据变更以json格式展示,并不符合我们的个性化需求,所以我们根据自己的需求定义了自己的反序列化器SqlDeserializationSchema,在此反序列器中我们做了如下实现:
1.把所有的操作分为DDL和DML语句
Envelope.Operation operation = Envelope.Operation.forCode((String) valueStruct.get(OPERATION));
2.把DML语句划分成不同类型的sql操作语句
public enum MysqlOperate {
// ddl
DDL,
// dml
INSERT,
UPDATE,
DELETE,
// 未定义
UNDEFINE,
;
}
在基于查询的CDC中只会转化成INSERT语句,在基于日志的CDC中数据的操作记录会转化为INSERT、UPDATE、DELETE语句进行处理。
3.UPDATE和DELETE操作需要根据主键来进行操作
List <Field> primaryKeys = (((Struct) sourceRecord.key()).schema()).fields();
4.分析Flink CDC给我们的数据格式,按照不通类型进行分析
- 基于查询的CDC,使用JDBC查询的数据集
{"before":null,"after":{"config_id":1,"config_name":"Timothy Mendoza","config_key":"Paai6QCGyu","config_value":"RSVnKEpg7H","config_type":"U","create_by":"LnwDJwQL3l","create_time":997278609000,"update_by":"2008-09-20","update_time":1283015983000,"remark":"SvhrczrD0p"},"source":{"version":"1.5.4.Final","connector":"mysql","name":"mysql_binlog_source","ts_ms":0,"snapshot":"false","db":"xsy_flowreplay","sequence":null,"table":"sys_config_copy1","server_id":0,"gtid":null,"file":"","pos":0,"row":0,"thread":null,"query":null},"op":"r","ts_ms":1650611958990,"transaction":null}
- 基于binlog日志的CDC数据变更的数据集-INSERT
{"before":null,"after":{"config_id":2,"config_name":"Timothy Mendoza","config_key":"Paai6QCGyu","config_value":"RSVnKEpg7H","config_type":"U","create_by":"LnwDJwQL3l","create_time":997278609000,"update_by":"2008-09-20","update_time":1283015983000,"remark":"SvhrczrD0p"},"source":{"version":"1.5.4.Final","connector":"mysql","name":"mysql_binlog_source","ts_ms":1650612061000,"snapshot":"false","db":"xsy_flowreplay","sequence":null,"table":"sys_config_copy1","server_id":1,"gtid":null,"file":"binlog.000007","pos":653648221,"row":0,"thread":null,"query":null},"op":"c","ts_ms":1650612061677,"transaction":null}
- 基于binlog日志的CDC数据变更的数据集-UPDATE
{"before":{"config_id":1,"config_name":"Timothy Mendoza","config_key":"Paai6QCGyu","config_value":"RSVnKEpg7H","config_type":"U","create_by":"LnwDJwQL3l","create_time":997278609000,"update_by":"2008-09-20","update_time":1283015983000,"remark":"SvhrczrD0p"},"after":{"config_id":1,"config_name":"likenan","config_key":"Paai6QCGyu","config_value":"RSVnKEpg7H","config_type":"U","create_by":"LnwDJwQL3l","create_time":997278609000,"update_by":"2008-09-20","update_time":1283015983000,"remark":"SvhrczrD0p"},"source":{"version":"1.5.4.Final","connector":"mysql","name":"mysql_binlog_source","ts_ms":1650612078000,"snapshot":"false","db":"xsy_flowreplay","sequence":null,"table":"sys_config_copy1","server_id":1,"gtid":null,"file":"binlog.000007","pos":653648657,"row":0,"thread":null,"query":null},"op":"u","ts_ms":1650612078963,"transaction":null}
- 基于binlog日志的CDC数据变更的数据集-DELETE
{"before":{"config_id":1,"config_name":"tangrui","config_key":"Paai6QCGyu","config_value":"RSVnKEpg7H","config_type":"U","create_by":"LnwDJwQL3l","create_time":997278609000,"update_by":"2008-09-20","update_time":1283015983000,"remark":"SvhrczrD0p"},"after":null,"source":{"version":"1.5.4.Final","connector":"mysql","name":"mysql_binlog_source","ts_ms":1650612156000,"snapshot":"false","db":"xsy_flowreplay","sequence":null,"table":"sys_config_copy1","server_id":1,"gtid":null,"file":"binlog.000007","pos":653649872,"row":0,"thread":null,"query":null},"op":"d","ts_ms":1650612157103,"transaction":null}
- 基于binlog日志的CDC数据结构变更的数据集-新增表字段
{"source":{"version":"1.5.4.Final","connector