MySQL 5.7跨集群拆分迁移
一、背景
一些业务时间久了之后,会进行一些业务逻辑的修改,通常也需要数据库的拆分迁移。这里假设源数据库为集群A(每个集群中仅有一个数据库,这里集群A就代表了数据库A),目标数据库为数据库B、C,之前甲业务和乙业务在一起都使用集群A,一段时间后,甲业务和乙业务要分开,乙业务以后使用集群B和集群C,这时,需要对数据库A进行拆分,将数据库A的table1、table2拆分迁移到B库,数据库A的table3、table4、table5拆分迁移到C库。针对这种情况,大致拆分迁移流程如下:
本次所有涉及数据库均为MySQL5.7 版本,并开启了gtid模式复制
将B库和C库所需数据分别从A库中导出
将导出数据导入B库和C库,并在B集群和C集群的主库上同步A库对应数据
业务迁移对应表的读写流量到新库中
断开B库和C库到A库的同步
将A库中table1、table2等表改名,保留一段时间后删除源数据,完成数据库的拆分迁移
大致结构图如下:
二、迁移过程
根据拆分流程分步骤进行数据库的拆分迁移,现将过程中遇到的问题作为记录总结:
1、数据导出与导入
拆分迁移过程中由于数据量不大,使用了原生的mysqldump导出数据,并在mysql中直接使用source导入数据(若数据量大的话可以使用mydumper和myloader),由于本次所有数据库均为MySQL5.7,并开启了gtid,所以默认mysqludmp导出数据,其中会包含gtid_purged参数,这个参数主要是用来恢复一个集群的从库,使其可以作为源集群的主库,但仅适用于新建库,若库已使用了一段时间,需要通过reset master;
置空gitd_executed参数才可配置,如果当前库仅作为源集群的从库,可以执行reset master;
,但本次数据库迁移过程中需要导入数据的并不仅是A库的从库,它也是另外一个集群的主库,主库若执行了reset master;
操作,该集群的从库就全部作废,需要重新搭建从库。
故本次迁移在导入数据之前需要将数据文件中关于gtid设置的部分全部注释掉,保证目标集群数据一致性。若直接将带有gtid信息的数据文件导入,由于导入主库的gtid_executed不为空,所以无法设置gtid_purged,集群主库可以导入数据,但从库并不会同该主库同步数据。
2、数据同步
传统主从对开启GTID模式的数据库进行同步,需要执行以下change master to
语句来建立主从关系,但master_auto_position
是需要在指定gtid_executed的情况下自动同步其主库数据,上面也描述了,在已经有数据的集群主库上指定该值,需要执行reset master;
操作,但该操作会初始化master上的gtid及binlog数据,从库便无法继续正常同步,故该集群从库便需要重新搭建。若从库上有业务流量,便会出现读取数据有误。
CHANGE MASTER TO
MASTER_HOST='x.x.x.',
MASTER_PORT=xxxx,
MASTER_USER='user',
MASTER_PASSWORD='xxxx',
master_auto_position=1;
为了解决从库需要重新搭建的问题,这次使用了pingcap的syncer工具来进行同步。具体可参考官方网站:https://pingcap.com/docs-cn/v3.0/syncer-overview/
根据本次需求,需要从数据文件中得到导出数据时源数据库的gtid_purged值,并编写配置文件,具体流程如下:
# 以下为syncer工具数据目录,syncer为从官方网站下载的工具包中的syncer命令文件
# config.toml为配置文件
# syncer.meta为源库gtid或binlog file和position信息
[root@xxxx soft]# tree syncer/
syncer/
├── config.toml
├── syncer
└── syncer.meta
# syncer.meta文件如下:若开启了gtid复制,则仅写binlog-gtid即可,若是binlog file复制,则写入binlog-name和binlog-pos即可
# 该文件设置后不再需要手动更改
# 可以将不需要的值设置为空,也可直接删除不需要的行
# 该文件会在同步过程中自动更新,若同步手动或异常断开,重新开启同步命令后,会根据该文件的值自动继续同步
[root@xxxx syncer]# cat syncer.meta
binlog-name = ""
binlog-pos =
binlog-gtid = "xxxx:1-xx"
[root@xxxx syncer]# cat config.toml
log-level = "info"
log-file = "syncer.log"
log-rotate = "day"
## 和MySQL配置文件一样,若同时开启了多个syncer,该server-id不能相同
server-id = 343501
## meta 文件地址
meta = "./syncer.meta"
worker-count = 16
batch = 100
flavor = "mysql"
# 可以设置仅同步那个库,也可根据replicate-ignore-db设置不同步哪些库,支持正则表达配置
#replicate-do-db = ["dbzz_liveoperation"]
# 仅往哪些表进行数据同步
[[replicate-do-table]]
db-name ="dbzz_liveoperation"
tbl-name = "t_live_activity_apply"
[[replicate-do-table]]
db-name ="dbzz_liveoperation"
tbl-name = "t_live_lottery"
# 当源库和目标库库名不一致时,可根据route-rules进行rewrite,pattern-schame表示源库名,target-schema表示目标库名
[[route-rules]]
pattern-schema = "dbzz_opentobtool"
target-schema = "dbzz_liveoperation"
# 源集群信息
[from]
host = "x.x.x.x"
user = "user"
password = "xxxxxxxx"
port = 3306
# 目标集群信息
[to]
host = "xx.xx.xx.xx"
user = "root"
password = "xxxxxxx"
port = 4000
# 启动syncer命令,启动后会自动生成syncer.log语句,这里直接写了一个启动脚本执行
# 若开启了gtid模式,启动时需要指定--enable-gtid,否则指定config即可
[root@xxxx syncer]# cat start.sh
nohup ./syncer -config config.toml --enable-gtid &
# 若结果中类似下面情况,则已经正常开启同步,event和tps不都为0
[root@xxxx syncer]# tailf syncer.log
2020/05/17 01:14:38 meta.go:135: [info] save position to file, binlog-name:mysql-bin.000008 binlog-pos:515618735 binlog-gtid:7acc0fb9-505a-11e9-aed3-246e96be16a0:1-2333842
2020/05/17 01:14:43 syncer.go:955: [info] [syncer]total events = 1459, tps = 0.000000, master-binlog = (mysql-bin.000008, 515623866), master-binlog-gtid=7acc0fb9-505a-11e9-aed3-246e96be16a0:1-2333854, syncer-binlog = (mysql-bin.000008, 515623866), syncer-binlog-gtid = 7acc0fb9-505a-11e9-aed3-246e96be16a0:1-23338543、数据校验
3、数据校验
传统使用change master to
命令通过主从进行数据同步后,我们可以通过show slave status;
命令观察Seconds_Behind_Master
值来检查是否已经同步完成,该值为0表示已经和源数据同步。但使用syncer命令变不能通过该方式进行校验,官方下载的工具包提供了sync_diff_inspector工具来提供数据校验服务,下面根据本次需求来设置了配置文件,其他情况可参考官方文档:
# 其中config.toml为配置文件
# sync_diff_inspector为进行数据校验的命令文件
[root@xxxx sync-diff]# tree .
.
├── config.toml
└── sync_diff_inspector
[root@xxxx sync-diff]# cat config.toml
# 日志级别,可以设置为 info、debug
log-level = "info"
# sync-diff-inspector 根据主键/唯一键/索引将数据划分为多个 chunk,
# 对每一个 chunk 的数据进行对比。使用 chunk-size 设置 chunk 的大小
chunk-size = 1000
# 检查数据的线程数量
check-thread-count = 4
# 抽样检查的比例,如果设置为 100 则检查全部数据
sample-percent = 100
# 通过计算 chunk 的 checksum 来对比数据,如果不开启则逐行对比数据
use-checksum = true
# 如果设置为 true 则只会通过计算 checksum 来校验数据,如果上下游的 checksum 不一致也不会查出数据再进行校验
only-use-checksum = false
# 是否使用上次校验的 checkpoint,如果开启,则只校验上次未校验以及校验失败的 chunk
use-checkpoint = true
# 不对比数据
ignore-data-check = false
# 不对比表结构
ignore-struct-check = false
# 保存用于修复数据的 sql 的文件名称,若数据与源库已经同步完成的话,该文件需为空,否则表示与源数据未同步
fix-sql-file = "fix.sql"
# 需要校验的表信息
[[check-tables]]
schema = "dbzz_liveoperation"
tables = ["t_live_activity_apply", "t_live_lottery"]
# 源库名和目标库名分别是什么
[[table-rules]]
schema-pattern = "dbzz_opentobtool"
target-schema = "dbzz_liveoperation"
# 源库信息,用户密码等情况都根据实际情况填写即可
[[source-db]]
host = "x.x.x.x"
port =3306
user = "dba"
password = "xxxxxx"
# 目标库信息
[target-db]
host = "xx.xx.xx.xx"
port = 4000
user = "dba"
password = "xxxxxxx"
# 执行命令,写入了start.sh执行,可以直接执行
[root@xxxx sync-diff]# cat start.sh
nohup ./sync_diff_inspector --config=config.toml &
# 由于这里使用了nohup执行,所以结果会放入nohup.out中,并生成了fix.sql文件,当fix.sql文件为空,并且最终结果类似下面情况,则表示同步完成,否则都表示同步有异常,根据输出结果检查即可
[root@xxxx sync-diff]# tailf nohup.out
[2020/05/15 16:06:45.195 +08:00] [INFO] [main.go:84] ["check report"] [report="\ncheck result: pass!\n10 tables' check passed, 0 tables' check failed.\n\ntable: dbzz_liveoperation.t_live_red_config_rel\ntable's struct equal\ntable's data equal\n\ntable: dbzz_liveoperation.t_live_activity_apply\ntable's struct equal\ntable's data equal\n\ntable: dbzz_liveoperation.t_live_red_config\ntable's struct equal\ntable's data equal\n\ntable:dbzz_liveoperation.t_live_reward\ntable's struct equal\ntable's data equal\n\ntable: dbzz_liveoperation.t_live_lottery\ntable's struct equal\ntable's data equal\n\n"]
[2020/05/15 16:06:45.195 +08:00] [INFO] [main.go:71] ["check data finished"] [cost=1.032005913s]
[2020/05/15 16:06:45.195 +08:00] [INFO] [main.go:65] ["test pass!!!"]
三、总结
在之前MySQL跨集群拆分迁移过程中,使用的是官方提供的CHANGE REPLICATION FILTER REPLICATE_REWRITE_DB
和CHANGE REPLICATION FILTER REPLICATE_WILD_DO_TABLE
的方法,这种迁移方法在MySQL5.5和MySQL5.6中需要写入配置文件并重启MySQL生效,在开启了gtid模式的MySQL5.7版本中,又需要执行reset master;
设置gtid_purged从而使change master to
开启主从成功,所以需要重新搭建目标集群的所有从数据库,导致无法平滑稳定的进行数据库的拆分,整个拆分迁移流程无法自动化实现。本次使用syncer工具进行拆分迁移,整个过程中源数据库和目标数据库都无需重启或变动,后续可以考虑自动化实现跨集群拆分迁移。