一、背景
一些业务时间久了之后,会进行一些业务逻辑的修改,通常也需要数据库的拆分迁移。这里假设源数据库为集群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 TOMASTER_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.metabinlog-name = ""binlog-pos =binlog-gtid = "xxxx:1-xx"[root@xxxx syncer]# cat config.tomllog-level = "info"log-file = "syncer.log"log-rotate = "day"## 和MySQL配置文件一样,若同时开启了多个syncer,该server-id不能相同server-id = 343501## meta 文件地址meta = "./syncer.meta"worker-count = 16batch = 100flavor = "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.shnohup ./syncer -config config.toml --enable-gtid 若结果中类似下面情况,则已经正常开启同步,event和tps不都为0[root@xxxx syncer]# tailf syncer.log2020/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-23338422020/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、debuglog-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,如果开启,则只校验上次未校验以及校验失败的 chunkuse-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 =3306user = "dba"password = "xxxxxx"# 目标库信息[target-db]host = "xx.xx.xx.xx"port = 4000user = "dba"password = "xxxxxxx"# 执行命令,写入了start.sh执行,可以直接执行[root@xxxx sync-diff]# cat start.shnohup ./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工具进行拆分迁移,整个过程中源数据库和目标数据库都无需重启或变动,后续可以考虑自动化实现跨集群拆分迁移。