注册 登录
  • 欢迎访问"运维那点事",推荐使用Google浏览器访问,可以扫码关注本站的"微信公众号"。
  • 如果您觉得本站对你有帮助,那么可以扫码捐助以帮助本站更好地发展。

MySQL备份恢复:多线程mydumper工具

MySQL 彭东稳 3022次浏览 已收录 1个评论

一、mydumper&myloader介绍

mydumper&myloader是用于对MySQL数据库进行多线程备份和恢复的开源 (GNU GPLv3)工具。开发人员主要来自MySQL、Facebook和SkySQL公司,目前由Percona公司开发和维护,是Percona Remote DBA项目的重要组成部分,包含在Percona XtraDB Cluster中。mydumper的第一版0.1发布于2010.3.26,最新版本0.9.1发布于2015.11.06。

mydumper特点:

1:C语言写的,轻量级。

2:多线程备份,执行速度比mysqldump快N倍,并支持行级chunk备份。

3:事务性和非事务性表一致的快照(适用于0.2.2以上版本)。

4:快速的文件压缩。

5:支持导出binlog。

6:多线程恢复(适用于0.2.1以上版本)。

7:以守护进程的工作方式,定时快照和连续二进制日志(适用于0.5.0以上版本)。

8:支持从库备份时记录主库的position,且不需要关闭slave sql线程(mysqldump需要关闭sql线程);并且支持同时记录主从position(mysqldump不支持)。

9:开源 (GNU GPLv3)。

导入导出测试

下图是在SSD和HDD存储介质上将mydumper和同为逻辑备份的MySQL官方mysqldump所做的性能对比测试,测试对象为网易博客库:

MySQL备份恢复:多线程mydumper工具

可以发现,在备份时间上,mydumper性能比mysqldump好将近1倍,通过分析不难发现,两者性能的差距主要跟备份实例的IO性能以及mydumper开启的线程数有关系,如果外部实例的IO性能很差,那么可能mysqldump单线程就能够将IO吃满,那改为使用mydumper也无法带来性能提升,但如果实例的IO性能较好,通过增加备份线程数(测试所用为2线程),就可能带来成倍的性能提升。导入时间分别使用与mydumper配合使用的myloader多线程sql导入工具以及mysqldump对应的source命令得到。相应的,在IO性能较好的SSD上,myloader的多线程优势体现明显,比source的单线程执行时间上节省将近2倍(测试所用为4线程)。不过在HDD上,由于其本身性能原因,myloader带来的效益变小。

二、多线程导出原理

mydumper原理与mysqldump原理类似,最大的区别是引入了多线程备份,每个备份线程备份一部分表,当然并发粒度可以到行级,达到多线程备份的目的。这里要解决最大一个问题是,如何保证备份的一致性,其实关键还是在于FTWRL。对于非Innodb表,在释放锁之前,需要将表备份完成。对于Innodb表,需要确保多个线程都能拿到一致性位点,这个动作同样要在持有全局锁期间完成,因为此时数据库没有读写,可以保证位点一致。所以基本流程如下:先备份no-innodb表数据在备份innodb数据。

在MySQL 5.7版本中,官方发布了一种新的备份工具mysqlpump,也是多线程的,其实现方式给人耳目一新的感觉,但遗憾的是其仍为表级别的并行且不支持position点的记录。而mydumper能够实现记录级别的并行备份,其备份框架由主线程和多个工作线程组成,备份流程可见下图:

MySQL备份恢复:多线程mydumper工具

主线程负责建立数据一致性备份点、初始化工作线程和为工作线程推送备份任务:

  • 对备份实例加读锁,阻塞写操作以建立一致性数据备份快照点,记录备份点BinLog信息;
  • 创建工作线程,初始化备份任务队列,并向队列中推送数据库元数据(schema)、非InnoDB表和InnoDB表的备份任务;

工作线程负责将备份任务队列中的任务按顺序取出并完成备份:

  • 分别建立与备份实例连接,将session的事务级别设置为repeatable-read,用于实现可重复读;
  • 在主线程仍持有全局读锁时开启事务进行快照读,这样保证了读到的一致性数据与主线程相同,实现了备份数据的一致性;
  • 按序从备份任务队列中取出备份任务,工作线程先进行MyISAM等非InnoDB表备份,再完成InnoDB表备份;这样可以在完成非InnoDB表备份通知后主线程释放读锁,尽可能减小对备份实例业务的影响;

mydumper的记录级备份由主线程负责任务拆分,由多个工作线程完成。主线程通过将表数据拆分为多个chunk,每个chunk作为一个备份任务。表数据拆分方式如下所述:mydumper优先选择主键索引的第一列作为chunk划分字段,若不存在主键索引,则选择第一个唯一索引作为划分依据,若还不存在,则选择区分度(Cardinality)最高的任意索引。如果还是无法满足,则只能进行表级的并行备份。在确定了chunk划分字段后,先获取该字段的最大和最小值,再通过执行“explain select field from db.table”来估计该表的记录数,最后根据所设的每个任务(文件)记录数来将该表划分为多个chunk。如下图所示:

MySQL备份恢复:多线程mydumper工具

以上描述可知,mydumper并不能保证记录级备份时,每个备份任务中的记录数是相同的。另外,目前记录级备份存在一个bug:所用索引字段为负数时主线程会进入死循环无法退出,导致备份失败。https://bugs.launchpad.net/mydumper/+bug/1418355

与mysqldump另一个不同是, mydumper为每个备份任务建立至少一个备份文件。在0.9.1版本中,文件类型包括schema-create、schema、schema-post文件等表元数据文件分别用保存建数据库语句、建表语句(包括触发器)、函数/存储过程/事件定义语句等;数据文件可以根据用户设置为固定大小或固定记录数的文件。这样便以进行更细粒度的数据恢复和数据删除,比如可以在myloader的时候仅选择某几个数据库/表进行恢复。

三、网易对mydumper的改进

mydumper是一款优秀的备份工具,但也存在不足,包括多线程导出数据对实例业务的影响、逻辑备份方式对热点数据污染和持锁时对业务的阻塞等。网易RDS在mydumper实践中对其进行了多方面的优化。

多线程备份固然好,但在进行备份时往往数据库还在正常提供对外服务,多线程全表select数据会占用很大部分的系统IO能力,导致正常的业务IO性能下降,严重时甚至会使数据库连接爆掉。通过为mydumper增加负载自适应能力来最大限度缓解对线上业务影响:工作线程在每次数据导出前,都会首先观察实例的当前负载情况,举MySQL状态Thread_connected为例,其反映的是目前已连接到该实例的请求数,如果该数值大于设定的阈值,则本次导出操作会暂停,直到数值小于阈值才会恢复,这样就起到了根据实例业务负载情况,灵活调整用于数据导出的线程数来适应线上业务负载的作用。

逻辑备份的全表select不可避免会污染InnoDB Buffer Pool的热点数据,缓存的热点数据被换出,降低了命中率的同时增大了业务的IO量,在使用mydumper时应尽量减小对Buffer Pool的影响;通过调整Buffer Pool的热点算法,使得热点数据尽可能不被换出。修改innodb_old_blocks_time和innodb_old_blocks_pct,用于将全表select进入Buffer Pool放在其old sublist中,同时减小old sublist块在Buffer Pool中的比例,起到最小化污染的作用。其原理详见http://dev.mysql.com/doc/innodb-plugin/1.0/en/idm47548325357504.html

在进行数据备份时,由于MyISAM表是非事务的,为了得到一致性的数据,导出MyISAM表需要全程持有读锁。在通常的MySQL实例中,MyISAM表数据都是很少的,所以持锁时间很短,但若有实例存在大量的MyISAM表数据,那么就会因持锁时间过长对业务的数据更新和插入造成影响。通过为mydumper增加持锁超时时间来避免该问题,所在数据备份过程中,持锁时间超过所设置时间,则mydumper返回失败,通过将MyISAM表转化为InnoDB表后再开始导出。

此外,在对大数据量数据库进行备份时,往往需要耗费较长时间,如果能够实时了解备份进度,相信是一个很好的体验,为此,给mydumper增加了进度查询功能,能够查询mydumper所需执行的所有备份任务数、当前已经完成的备份任务数及每个备份任务所花费时间。

四、安装mydumper&myloader

下载mydumper

安装依赖

解压安装

安装完成后会生成2个工具:mydumper(备份),myloader(导入),你可以直接复制放入到bin目录下。

五、mydumper&myloader语法

mydumper(备份工具)

myloader(恢复工具)

测试:基本用法

备份sbtest数据库到/backup目录中,查看整个备份产生的执行语句(开启general log),是否多线程(开启5个):

如果你使用mysqldump的话可以看到整个过程都只有一个线程,而使用mydumper可以看到开启了多线程,SELECT阶段根据我们设置的线程数同时工作了。

分析:mydumper把数据和表结构分开备份,并且把二进制日志备份出来单独放到一个文件中。

metadata:元数据记录备份开始和结束时间,以及master binlog position点(默认记录),如果在slave上备份默认会记录master binlog position及slave binlog position点,并且不会关闭SQL线程,这个很友好,比mysqldump及mysqlpump都强。

table.sql:每个表一个文件。

table-schema.sql:表结构文件。

daemon mode:在这个模式下,有五个目录0,1,binlogs,binlog_snapshot,last_dump。

备份目录是0和1,间隔备份,如果mydumper因某种原因失败而仍然有一个好的快照,当快照完成后,last_dump指向该备份。

还原:还原到另一台服务器,先建立要还原的数据库(sbtest)

还原和备份一样也是使用多线程进程还原的。

进一步测试:测试一些常用的参数

1):备份指定表(-T),并且不要导出表结构(-m)

2)压缩备份文件(-c),备份binlog(-b),正则表达式备份表(-x)

如上所示,备份文件已经是压缩的了(用gzip -d 解压),并且备份出了tmp.*匹配出来的所有表,二进制日志也被备份到了binlog_snapshot文件中,并且也是被压缩的。

这里说明下备份指定数据库的方法:–regex正则匹配

3)还原,表存在先删除(-o):这里需要注意,使用该参数,备份目录里面需要有表结构的备份文件。

mydumper支持一张表多个线程以chunk的方式批量导出,参数-r:试图用行块来分割表,该参数关闭–chunk-filesize参数。如:

表示每个线程用300000行块来分割表,通过show processlist可看到5个线程备份。大致方式方式如下记录:

这个可以更好地提升数据库备份速度,不管数据库里是否有大表,而MySQL官方的mysqlpump虽说也是多线程但是只能针对多表多线程,遇到大表时也是无能为力。

最后再来说一下,mydumper这个神器,支持在从库备份并且同时记录master binlog position及slave binlog position,并且不需要关闭SQL线程,很强。有了这个功能就可以大胆在从库备份,从而达到随意做从库,无论是依赖master还是slave都可以做从库。相关的信息都存在metadata文件中,一般信息如下:

关于mydumper到这里就结束了。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (2)or分享 (0)
关于作者:

您必须 登录 才能发表评论!

(1)个小伙伴在吐槽