网易云 MySQL实例迁移的技术实现

社区编辑2018-05-18 14:48

我们把数据库里部分或全部 Schema和数据迁移到另一个实例的行为称为实例迁移,将导出数据的实例称为源实例,导入数据的实例称为目标实例。
 
根据迁移数据库类型的不同,可以分为同种数据库之间的迁移,如从 MySQL迁到 MySQL;和跨数据库类型的迁移,如从 Oracle迁移到 MySQL等。
 
本文将介绍网易云基础服务(蜂巢) RDS实例迁移功能的实现,并探讨如何高效完成实例迁移任务。
 

使用场景

 
那么,为什么要进行 MySQL实例迁移呢?不同场景下分别该如何迁移?实例迁移的场景归纳起来主要有以下几种:
 
1、从自建实例迁移到 RDS:
 
在云服务还未充分推广时,存在大量自建数据库实例,以网易公司为例,在公司业务全面上云之前,网易博客、网易邮箱等产品的数百个 MySQL实例都是直接部署在物理服务器上,而随着业务的扩展,必然要对实例进行扩容、升规格等操作。
 
相比自建实例,RDS实例在故障处理、在线扩容、升级等方面存在着天然的优势,所以,目前,网易绝大部分互联网产品的数据库均已使用实例迁移功能将实例迁移到了 RDS上;
 

 
2、从其他公有云平台迁移到 RDS:
 
网易云基础服务(蜂巢)RDS推出一年多的时间以来,有很多用户将部署在其他公有云平台上的 MySQL实例迁移到网易云基础服务(蜂巢)RDS上。我们对实例迁移功能进行统计发现,有 50%是用于迁移其他公有云的 MySQL实例。
 

实例迁移技术实现

 
在设计实例迁移功能前,我们对业界公有云进行了充分调研,仅有两家主流公有云平台提供实例迁移功能,那么为什么仅两家呢,主要是因为提供在线实例迁移功能需要解决一系列问题,概括起来有以下几点:
 
1.如何快速地对源实例进行一致性数据备份?
 
2.如何处理备份过程中对源实例业务的影响?
 
3.如何快速地将备份导入到目标实例?
 
4.如何同步源实例的增量数据到目标实例?
 
5.如何确保实例迁移高效完成?
 
下面逐条解析网易云基础服务(蜂巢) RDS是如何解决这些难题的。
 

1 多线程逻辑备份

 
我们解决第一个问题的方法是采用多线程逻辑备份的方式来进行源实例一致性数据导出。
 
MySQL的数据备份工具有很多,逻辑备份工具包括经典的 mysqlpump,MySQL 5.7版本新推出的 mysqlpump,Percona开源工具 mydumper;物理备份主要是 Percona的 xtrabackup工具。
 
俗话说没有最好的,只有最合适的,那么在这些备份工具中,哪种工具最适合用于进行实例迁移呢?我们的答案是 mydumper
 
首先我们排除了 xtrabackup,虽然物理备份在性能上有优势,但其无法远程备份源实例,在进行实例迁移时,我们不可能要求用户赋予操作源实例服务器的权限,尤其在迁移其他公有云平台的 RDS实例时更不现实。此外,物理备份产生的备份数据往往比逻辑备份导出的数据更大,因为 xtrabackup直接拷贝物理文件,而逻辑备份是导出 SQL语句。
 
下面是我们对几种备份工具的对比测试结果,可供参考:
 

 
排除了物理备份后,还有三个选项:mysqldump、mysqlpump和 mydumper。我们最终选择了 mydumper,因为 mydumper是多线程的。
 
等等!了解 MySQL的同学会质疑,mysqlpump也是多线程的啊?对,mysqlpump的多线程思想甚至比 mydumper更先进(详见参考文献1和2),但 mysqlpump是表级的并发,且还不成熟,而 mydumper是记录级的并发,针对单个大表的场景,更容易发挥多线程优势。
 
也许你会好奇,mydumper是如何实现记录级的多线程一致性备份的,其备份流程图如下:
 

 
mydumper由主线程和多个工作线程配合完成数据一致性备份,主线程执行 FTWRL或 Lock Tables tablelist Read阻塞写操作来建立一致性备份点并记录当前 BinLog和 GTID。工作线程在主线程仍持有锁的情况下将各 session的事务级别设置为可重复读(repeatable-read),并开始进行快照读。由于此时各表无法进行数据写入或更新,所以工作线程快照读的数据就是主线程建立一致性备份点的数据。待所有工作线程均已开始快照读后,如果不存在 MyISAM等非事务性表,主线程即可释放读锁(mydumper原理的详细分析详见参考文献3)。
 

2 业务负载监控与调整

 
无论是物理备份还是逻辑备份,都会或多或少地对数据库线上业务造成影响。如何处理备份过程中对源实例业务的影响是我们需要解决的第二个问题。
 
网易云基础服务(蜂巢) RDS实例的设计原则是线上业务永远比迁移任务更重要。由于无法有效了解源实例所在服务器层的监控数据,我们在 MySQL数据库层进行大量的优化来降低影响。包括引入持锁时间超时机制、基于业务负载智能调整导出并发度和 InnoDB Buffer Pool(BP)污染控制等。
 
如前所述,为了能够得到一致性的数据,各种备份工具,包括 xtrabackup和 mydumper,都需要有个短暂给源实例加读锁的过程,正常情况下短暂,但也会有例外,如源实例中存在数据量较大的 MyISAM表时,持锁时间会变长。
 
为了能够避免持锁时间过长导致业务的写操作被阻塞,使用网易云基础服务(蜂巢) RDS进行实例迁移时,用户可以选择允许持有读锁的最长时间,如下图所示,如果超过该阈值时间,会无条件解锁并让迁移操作失败,用户可以选择在业务低峰期进行重试。
 

 
在顺利加锁建立一致性快照并解锁后,就进入到各种 Schema和表数据的导出环节,用户应根据源实例的线上业务负载和实例的服务器 IO能力来合理选择导出数据的并发线程数,如上图所示。
 
业务负载并不总是可以预测的,但业务总是最重要的,那么当短暂的业务高峰上来时,我们希望将服务器有限的 IO能力还给业务,而不是用在迁移上。网易云基础服务(蜂巢) RDS提供了负载监控阈值选项,在业务负载超过该阈值时,会暂停迁移操作,直到负载重新低于阈值。如果用户选择了多线程导出,则能够根据业务负载动态调整线程个数,确保在业务优先的前提下尽可能快速地完成数据导出操作。
 
下图为基于业务负载自适应调整导出线程的例子。
 

 
在逻辑导出的过程中,还会根据用户提供的迁移账号权限,选择性调整 InnoDB BP参数来最大限度减小迁移连接的查询操作对 BP热点数据的污染。尽可能将因迁移而进入 BP的数据保留在 BP的 LRU List冷数据一侧,并尽快被替换出 BP(详见参考文献4)。当然,设置 BP的参数需要账号有 Super权限,对于公有云上的源实例,无法进行该项优化。
 

3 多线程数据导入

 
使用与 mydumper配套的多线程恢复工具 myloader来将备份的数据导入到目标 RDS实例上,myloader执行流程如下图所示。
 

 
由于此时目标实例没有负载,所以可以尽可能调大导入并发线程数,将目标实例的 IO能力吃满。此外,在数据导入时,我们通过关闭 slow log和 binary log,将 innodb_flush_log_at_trx_commit设置为 0来最大限度提高导入性能,在完成数据导入后再将对应的参数调整为原值。这是我们解决第三个问题的方法。
 

4 并行过滤复制

 
在完成数据导入后,对于全量迁移的场景,迁移就结束了。若选择增量迁移,还需将数据导出和导入时在源实例上产生的增量数据(Update/Delete)也迁移到目标实例,我们采用 MySQL复制的方式来同步这些数据。
 
由于 MySQL 5.5、5.6和 5.7版本的复制存在较大差别,我们根据源实例的版本选择对应的目标实例版本。对于 MySQL 5.5及更低版本的源实例,选择网易开源 MySQL版本 InnoSQL 5.5.30作为目标 RDS实例版本,对于 MySQL 5.6和 5.7,选择 InnoSQL 5.7.12为目标实例版本。进行上述版本配对的原因在于:
 
一是希望用户尽可能采用 MySQL最新的稳定版本 5.7,因为 MySQL 5.7是有史以来最好的版本,带来了众多优秀的特性,包括基于 GTID的复制、sys表等,同时相比之前的版本,解决和优化了大量缺陷或不足。
 
二是能够更加方便地配置复制。MySQL 5.7版本提供了基于 GTID和基于 BinLog两套复制机制,针对源实例不同的复制配置,能无缝适配。用户在迁移源实例时,可选择迁移实例上全部数据库,也可选择仅迁移部分数据库,MySQL 5.7版本可使用新增的 “CHANGE REPLICATION FILTER” 语法在线进行过滤复制设置而无需重启 mysqld。
 
由于 MySQL 5.5及更低版本无法满足 MySQL 5.7版本与之建立复制所需的实例 UUID,所以目标实例使用 InnoSQL 5.5.30版本。当然,相比社区版 MySQL 5.5.30,InnoSQL 5.5.30实现了在线过滤复制功能。
 
我们采用并行复制技术来提高增量数据同步的效率,快速缩短主从复制延迟。由于 MySQL 5.6版本 GTID特性并不完善,在将其迁移到 MySQL 5.7版本时,采用基于 DATABASE的并行复制方式,避免 LOGICAL_CLOCK并行复制时由于 GTID EVENT未记录并行信息导致复制出错的 bug。这样,第四个问题也得到了解决。
 

如何高效完成迁移

 
相信大家都认同,实例迁移是个重型操作,谁都不会闲来无事对线上数据库来一把实例迁移。既然决定要进行实例迁移,那么就希望能够一次性完成迁移,避免来回折腾。
 
如何确保高效地迁移就显得尤为重要,用户需先进行迁移评估并完成准备工作,网易云基础服务(蜂巢) RDS通过迁移预检查、提供出错重试等措施来提高迁移成功率。
 

1、迁移评估和准备

 
用户首先需在迁移前做好评估工作,包括选择业务低峰期进行迁移,这样既最小化对业务的影响同时也能够提高迁移速度;确认业务连接数据库的配置能够进行一次性切换,缩短切换所需时间,同时避免部分业务逻辑连接源库,另一部分连接目标库导致数据不一致;其次,根据所迁移的数据量,合理选择目标实例的存储空间,避免因为目标实例空间不足导致失败;最后,还需要创建满足迁移要求的数据库账号。
 

2、预检查

 
我们希望在开始迁移前就发现所有可能引起迁移失败的因素并纠正。迁移预检查是重要手段,主要包括用户在源实例创建的迁移 MySQL账号权限检查、MySQL参数设置检查。
 
迁移权限检查用于确认迁移账号是否能够顺利完成迁移操作,主要包括对数据库定义、表定义,视图、触发器、存储过程和函数等 Schema的查看权限;对所选中数据库中表的 Lock Table权限,及表中数据 Select权限;如果选择增量迁移,则还需检查账号是否具备 Replication slave和 Replication client权限等。通过查询源实例的 MySQL、information_schema或 performance_schema等系统库来检查迁移所需权限。
 
MySQL参数检查主要针对需要做增量数据同步的场景,如果用户选择增量迁移,源实例需正确设置 server_id和 log_bin等参数。如果在预检查中发现错误,会给出明确的提示,引导用户进行参数调整后再重新进行预检查。
 

3、错误重试

 
在迁移过程中,提供了进度显示功能,如下所示:
 

 
迁移的每个阶段都会有带进度条的百分比显示,并周期性自动刷新。同时还会显示整体的迁移进度,方便用户随时查看。若在数据导出或导入等阶段发生错误,则会提示错误信息,一般出现迁移错误的原因主要是由于存在 MyISAM表导致持锁时间超时,根据错误信息可以对迁移参数进行针对性修改后进行重试,无需重新开始迁移。
 

4、结束迁移

 
在确认目标实例和源实例间没有复制延迟后,就可以结束迁移并将业务的 IP切换为目标实例 IP。当然,在 IP切换前,请确认已经在网易云基础服务(蜂巢) RDS实例上创建业务访问所需的数据库账号并赋予合适的权限。
 
参考文献:
 
http://www.innoMySQL.com/article/25383.html
http://MySQLserverteam.com/introducing-MySQLpump/
http://www.innoMySQL.com/article/25456.html
http://dev.MySQL.com/doc/refman/5.7/en/innodb-performance-midpoint_insertion.html
http://dev.MySQL.com/doc/refman/5.7/en/replication.html