作者:张鱼小丸子-PingCAP​

参考文档:​​https://github.com/moiot/gravity/blob/master/docs/2.0/03-inputs.md​



整体架构

Gravity 的测试调研 Gravity -> TiDB_github

Gravity 是一款数据复制组件,提供全量、增量数据同步,以及向消息队列发布数据更新。 使用场景

  • 大数据总线:发送 MySQL Binlog,Mongo Oplog 的数据变更到 kafka 供下游消费
  • 单向数据同步:MySQL --> MySQL 的全量、增量同步
  • 双向数据同步:MySQL <–> MySQL 的双向增量同步,同步过程中可以防止循环复制
  • 分库分表到合库的同步:MySQL 分库分表 --> 合库的同步,可以指定源表和目标表的对应关系
  • 在线数据变换:同步过程中,可支持对进行数据变换


架构简介



单进程架构

单进程的 Gravity 采用基于插件的微内核架构,由各个插件围绕系统里的 ​​core.Msg​​ 结构实现输入到输出的整个流程。

各个插件有各自独立的配置选项。

Gravity 的测试调研 Gravity -> TiDB_mysql_02

如上图所示,系统总共由这几个插件组成:

  • Input 用来适配各种数据源,比如 MySQL 的 Binlog 并生成​​core.Msg​
  • Filter 用来对 Input 所生成的数据流做数据变换操作,比如过滤某些数据,重命名某些列,对列加密
  • Output 用来将数据写入目标,比如 Kafka, MySQL,Output 写入目标时,使用 Router 所定义的路由规则
  • Scheduler 用来对 Input 生成的数据流调度,并使用 Output 写入目标;Scheduler 定义了当前系统支持的一致性特性(当前默认的 Scheduler 支持同一行数据的修改有序
  • Matcher 用来匹配 Input 生成的数据。Filter 和 Router 使用 Matcher 匹配数据


基本功能测试



MySQL环境准备

Gravity 对源端 MySQL 的要求如下:

  • 开启 gtid 模式的 binlog
  • 创建 drc 账户,并赋予 replication 相关权限,以及 drc 数据库的所有权限
  • MySQL 源端、目标端相应的表需要创建好

MySQL 配置项如下所示

​[mysqld]​

​server_id=4​

​log_bin=mysql-bin​

​gtid-mode=ON​

​binlog_format=ROW​

drc 账户权限如下所示

​CREATE​​​ ​​USER​​​ ​​drc IDENTIFIED​​​ ​​BY​​​ ​​'xxx'​​​ ​​;​

​GRANT​​​ ​​SELECT​​​ ​​, RELOAD, LOCK TABLES, REPLICATION SLAVE, REPLICATION CLIENT,​​​ ​​INSERT​​​ ​​,​​​ ​​UPDATE​​​ ​​,​​​ ​​DELETE​​​ ​​ON​​​ ​​*.*​​​ ​​TO​​​ ​​'drc'​​​ ​​@​​​ ​​'%'​​​ ​​;​

​GRANT​​​ ​​ALL​​​ ​​PRIVILEGES​​​ ​​ON​​​ ​​drc.*​​​ ​​TO​​​ ​​'drc'​​​ ​​@​​​ ​​'%'​​​ ​​;​

上下游表结构创建

​--source​

​CREATE​​​ ​​TABLE​​​ ``test​​.​​​test_source_table​​(​

​\`\`id​​​ ​​int​​​ ​​(11),​

​PRIMARY​​​ ​​KEY​​​ ​​(​​​id​​)​

​) ENGINE=InnoDB​​​ ​​DEFAULT​​​ ​​CHARSET=utf8​

​--target​

​CREATE​​​ ​​TABLE​​​ ``test​​.​​​test_target_table​​(​

​\`\`id​​​ ​​int​​​ ​​(11),​

​PRIMARY​​​ ​​KEY​​​ ​​(​​​id​​)​

​) ENGINE=InnoDB​​​ ​​DEFAULT​​​ ​​CHARSET=utf8​



GO 环境配置

参考文档:​​配置好 Go 语言环境​

​wget https:​​​ ​​//dl​​​ ​​.google.com​​​ ​​/go/go1​​​ ​​.11.4.linux-amd64.​​​ ​​tar​​​ ​​.gz​

​tar​​​ ​​-C​​​ ​​/usr/local​​​ ​​-xzf go1.11.4.linux-amd64.​​​ ​​tar​​​ ​​.gz​

​mkdir​​​ ​​-p usr​​​ ​​/local/go/src/github​​​ ​​.com​​​ ​​/moiot/​​​ ​​&&​​​ ​​cd​​​ ​​usr​​​ ​​/local/go/src/github​​​ ​​.com​​​ ​​/moiot/​

​git clone https:​​​ ​​//github​​​ ​​.com​​​ ​​/moiot/gravity​​​ ​​.git​

​cd​​​ ​​gravity &&​​​ ​​make​

MySQL to MySQL

创建配置文件 mysql2mysql.toml

mysql2mysql.toml



同步测试

​##源端表及表数据​

​mysql> show databases;​

​+--------------------+​

​| Database |​

​+--------------------+​

​| information_schema |​

​| _gravity |​

​| mysql |​

​| performance_schema |​

​| sys |​

​|​​​ ​​test​​​ ​​|​

​| wk |​

​+--------------------+​

​7 rows​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql> use​​​ ​​test​

​Reading table information​​​ ​​for​​​ ​​completion of table and column names​

​You can turn off this feature to get a quicker startup with -A​

​Database changed​

​mysql> show tables;​

​+---------------------+​

​| Tables_in_test |​

​+---------------------+​

​| test_source_table_1 |​

​| test_source_table_2 |​

​+---------------------+​

​2 rows​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​​​ ​​select​​​ ​​* from test_source_table_1;​

​+-----+------+------+​

​|​​​ ​​id​​​ ​​| name | age |​

​+-----+------+------+​

​| 1 | NULL | NULL |​

​| 2 | NULL | NULL |​

​| 3 | NULL | NULL |​

​| 4 | NULL | NULL |​

​| 6 | NULL | NULL |​

​| 9 | NULL | NULL |​

​| 33 | NULL | NULL |​

​| 333 | wk | 12 |​

​| 334 | wk | 12 |​

​| 354 | wk | 12 |​

​+-----+------+------+​

​10 rows​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​​​ ​​select​​​ ​​* from test_source_table_2;​

​+-----+------+------+​

​|​​​ ​​id​​​ ​​| name | age |​

​+-----+------+------+​

​| 3 | NULL | NULL |​

​| 4 | NULL | NULL |​

​| 5 | NULL | NULL |​

​| 6 | NULL | NULL |​

​| 66 | NULL | NULL |​

​| 77 | GG | 18 |​

​| 81 | GG | 18 |​

​| 89 | GG | 18 |​

​| 166 | bb | 11 |​

​| 183 | bb | 11 |​

​+-----+------+------+​

​10 rows​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​

​目标端效果​

​mysql> show databases;​

​+--------------------+​

​| Database |​

​+--------------------+​

​| information_schema |​

​| mysql |​

​| performance_schema |​

​| sys |​

​|​​​ ​​test​​​ ​​|​

​+--------------------+​

​5 rows​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql> use​​​ ​​test​​​ ​​;​

​Reading table information​​​ ​​for​​​ ​​completion of table and column names​

​You can turn off this feature to get a quicker startup with -A​

​Database changed​

​mysql> show tables;​

​+-------------------+​

​| Tables_in_test |​

​+-------------------+​

​| test_target_table |​

​+-------------------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​​​ ​​select​​​ ​​* from test_target_table;​

​+-----+------+------+​

​|​​​ ​​id​​​ ​​| name | age |​

​+-----+------+------+​

​| 1 | NULL | NULL |​

​| 2 | NULL | NULL |​

​| 3 | NULL | NULL |​

​| 4 | NULL | NULL |​

​| 5 | NULL | NULL |​

​| 6 | NULL | NULL |​

​| 9 | NULL | NULL |​

​| 33 | NULL | NULL |​

​| 66 | NULL | NULL |​

​| 77 | GG | 18 |​

​| 81 | GG | 18 |​

​| 89 | GG | 18 |​

​| 166 | bb | 11 |​

​| 183 | bb | 11 |​

​| 333 | wk | 12 |​

​| 334 | wk | 12 |​

​+-----+------+------+​

​16 rows​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​

MySQL to TiDB

上游一库多表下游一库一表测试

配置文件

mysql2tidb2.toml

测试步骤

​--上游表​

​mysql> show tables;​

​+​​​ ​​---------------------+​

​| Tables_in_test |​

​+​​​ ​​---------------------+​

​| test_source_table_1 |​

​| test_source_table_2 |​

​+​​​ ​​---------------------+​

​2​​​ ​​rows​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_1;​

​+​​​ ​​------+---------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+---------+------+​

​| 2019 | pingcap | 3 |​

​+​​​ ​​------+---------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_2;​

​+​​​ ​​-----+------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​-----+------+------+​

​| 77 | GG | 18 |​

​| 81 | GG | 18 |​

​| 89 | GG | 18 |​

​| 166 | bb | 11 |​

​| 183 | bb | 11 |​

​+​​​ ​​-----+------+------+​

​5​​​ ​​rows​​​ ​​in​​​ ​​set​​​ ​​(0.01 sec)​

​--下游同步效果​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_target_table;​

​+​​​ ​​------+---------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+---------+------+​

​| 77 | GG | 18 |​

​| 81 | GG | 18 |​

​| 89 | GG | 18 |​

​| 166 | bb | 11 |​

​| 183 | bb | 11 |​

​| 2019 | pingcap | 3 |​

​+​​​ ​​------+---------+------+​

​6​​​ ​​rows​​​ ​​in​​​ ​​set​​​ ​​(1.91 sec)​

​--上游delete​

​mysql>​​​ ​​delete​​​ ​​from​​​ ​​test_source_table_2​​​ ​​where​​​ ​​name​​​ ​​=​​​ ​​'GG'​​​ ​​or​​​ ​​age=​​​ ​​'11'​​​ ​​;​

​Query OK, 5​​​ ​​rows​​​ ​​affected (0.00 sec)​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_2;​

​Empty​​​ ​​set​​​ ​​(0.00 sec)​

​--下游效果​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_target_table;​

​+​​​ ​​------+---------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+---------+------+​

​| 2019 | pingcap | 3 |​

​+​​​ ​​------+---------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.10 sec)​

​--上游 insert 在 test_source_table_2 中插入 id=2019 的记录,模拟主键冲突​

​mysql>​​​ ​​insert​​​ ​​into​​​ ​​test_source_table_2​​​ ​​values​​​ ​​(2019,​​​ ​​'tidb'​​​ ​​,2);​

​Query OK, 1 row affected (0.00 sec)​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_2;​

​+​​​ ​​------+------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+------+------+​

​| 2019 | tidb | 2 |​

​+​​​ ​​------+------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_1;​

​+​​​ ​​------+---------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+---------+------+​

​| 2019 | pingcap | 3 |​

​+​​​ ​​------+---------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​--下游效果​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_target_table;​

​+​​​ ​​------+------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+------+------+​

​| 2019 | tidb | 2 |​

​+​​​ ​​------+------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.31 sec)​

​-- 上游 update​

​mysql>​​​ ​​update​​​ ​​test_source_table_1​​​ ​​set​​​ ​​age = 999​​​ ​​where​​​ ​​name​​​ ​​=​​​ ​​'pingcap'​​​ ​​;​

​Query OK, 1 row affected (0.02 sec)​

​Rows​​​ ​​matched: 1 Changed: 1 Warnings: 0​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_1;​

​+​​​ ​​------+---------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+---------+------+​

​| 2019 | pingcap | 999 |​

​+​​​ ​​------+---------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​--下游结果​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_target_table;​

​+​​​ ​​------+---------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+---------+------+​

​| 2019 | pingcap | 999 |​

​+​​​ ​​------+---------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.05 sec)​

​-- 此时发现上游按照数据同样被 replace 了​

多库多表同步

gravity 可以同时启动多个,多个同步进程指向下游同一个库

默认 gravity 启动会使用 8080 端口,如果启动多个进程,需要改动默认端口

bin/gravity -config mysql2tidbtwo.toml -http-addr “:9090”

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_1;​

​+​​​ ​​----+------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​----+------+------+​

​| 0 | RAFT | 10 |​

​+​​​ ​​----+------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.00 sec)​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_source_table_2;​

​+​​​ ​​----+------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​----+------+------+​

​| 1 | TIKV | 2 |​

​+​​​ ​​----+------+------+​

​1 row​​​ ​​in​​​ ​​set​​​ ​​(0.01 sec)​

​--源库效果​

​mysql>​​​ ​​select​​​ ​​*​​​ ​​from​​​ ​​test_target_table;​

​+​​​ ​​------+---------+------+​

​| id |​​​ ​​name​​​ ​​| age |​

​+​​​ ​​------+---------+------+​

​| 0 | RAFT | 10 |​

​| 1 | TIKV | 2 |​

​| 2019 | pingcap | 999 |​

​+​​​ ​​------+---------+------+​

​3​​​ ​​rows​​​ ​​in​​​ ​​set​​​ ​​(7.50 sec)​

其它功能测试

filter 模块功能测试遇到 bug,摩拜正在修复

测试结果

  • 上下游同步成功 ,MySQL 到 TiDB 同步配置文件只需要修改对应 IP 和 port 就可以
  • 可以完成一库多表的合并
  • 同步之前需要在上下游创建好对应的 schema 以及表结构
  • DML 遇到主键冲突时会 replace
  • 多库合并仅限于同一数据库实例不同 schema ,跨实例合表有问题
  • MySQL 到 TiDB 同步配置文件只需要修改对应 IP 和 port 就可以


对比 TiDB-DM

Gravity 的测试调研 Gravity -> TiDB_数据_03