如果想知道如何使用xtrabackup,请翻阅之前的文章 xtrabackup核心文档
xtrabackup原理深入浅出
为什么要使用xtrabackup
逻辑备份 vs 物理备份
逻辑备份优点: 占用空间小,安全 逻辑备份缺点: 恢复速度慢 物理备份优点: 备份,恢复速度快 物理备份缺点: 占用空间大
如何选择,全凭业务场景
如果类似facebook这种量级(PB,EB),考虑成本,建议逻辑备份
如果只是上T 级别,建议物理备份
本人用的是物理备份,还有一个原因就是: 逻辑备份会遭到DDL的破坏
试想,晚上经常有DDL这样的操作,备份任务不可能随意调整,这样带来的运维成本很大
物理备份:InnoDB vs Myisam
Myisam引擎,只需要flush tables后,就可以随意拷贝文件 InnoDB可通过xtrabackup来拷贝文件
在5.6之前,我比较喜欢用Myisam引擎来做备份,原因是就是备份和恢复的速度以及随意拷贝的便利性 在5.6之后,由于可传输表空间的出现,让恢复单表变得简单,我更加愿意用InnoDB备份。关于可传输表空间,可阅读transportable tablespace详解 用Myisam的一个致命弱点是:如果要还原成innoDB表,需要转换表引擎,痛苦的开始 至于InnoDB和Myisam的选择,就不用纠结了,因为之后的MySQL会废弃掉Myisam
什么是一致性备份
简单来说,就是备份的数据都处在同一时间点
percona xtrabackup基本概念
innobackupex 是一个perl写的脚本,为了方便,封装了xtrabackup,所以一般备份都用innobackupex xtrabackup 只备份innoDB表 ,其余的都交给innobackupex 新版本如:2.4,2.3.xx 已经将innodbex用c 重写了,然后链接到xtrabackup
xtrabackup for 5.6 备份原理
innobackupex 调用 xtrabackup —suspend-at-end
xtrabackup 开始拷贝 innodb 数据文件,共享表空间
当xtrabackup拷贝完innodb文件后,会创建 xtrabackup_suspended_2
当innobackupex看到xtrabackup_suspended_2文件被创建后,进行加锁 a. FLUSH NO_WRITE_TO_BINLOG TABLES b. FLUSH TABLES WITH READ LOCK
innobackupex开始检查db server支持的特性,如:gtid,backup locks,changed page bitmap等
然后开始拷贝非innodb文件,以及表结构frm
当所有的文件都备份完成后,结束redo 事务的拷贝。
接下来释放锁 a. FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS b. UNLOCK TABLES
删除xtrabackup_suspended_2文件 , 并且退出
从上述流程图可以看出,FLUSH TABLES WITH READ LOCK【FTWRL】 这个时间点,就是备份恢复的时间点,即一致性点 因为数据再拷贝的过程中是不一致的,MySQL利用其自身的crash recover机制,用redo来保证最终一致性
这里,假设一种场景,InnoDB是16k的刷新,当刷新到4k的时候,拷贝线程来讲这个页拷贝走,这样不就人为的造成了一次partial-write了吗? 对于partial-write的场景,由于redo日志是物理逻辑的,通过redo没有办法恢复,这可如何是好呢?
这里是核心:
xtrabackup方式: xtrabackup里面做了很多事情,它在拷贝的过程中使用innodb的buf_page_is_corrupted()函数检查此页的数据是否正常,如果不正常,再重新拷贝,尝试N次重试之后还不行,就退出报错。
flush-method=O_direct,会直接从innodb buffer 写入到 磁盘,这个操作可能就会发生partial-write了。一旦发生这样的事情,MySQL是无法恢复的,所以,我们的备份必须保证脏页全部刷新完,也就是在备份程序中 a)必须保证没有写了 b)必须验证和检测Modified db pages是否等于0。 如果以上都ok,那么这个页肯定是完整的。除此之外,我们还有一招,就是多次rsync,确保完整。
flush-method=默认 , 默认的会先从innodb buffer 写入到 OS缓存,这个操作是原子的,由操作系统保证。
生产环境如何备份
备份的意图:备份master或者slave上的数据,然后传输到一个挂有大磁盘的服务器上,保留若干天
问题1,如何传输到其他服务器呢?
这个问题,xtrabackup也考虑到了,用xbstream + ssh 的方式,但是有一个致命的缺点,就是无法断点续传,如果网络断掉,那就悲剧
另外一个方式就是:先xtrabackup到本地一个目录,然后用rsync【支持断点续传】来传送到远端服务器。 这种方法的致命缺点是:本地磁盘需要浪费一半的空间,且copy了两遍备份数据
以上两种方式都存在缺陷,那么我们不得不思考第三种方式,是否可以直接copy到远端机器呢?
如果是在master上备份,那么直接copy是不行的,因为redo一直再变且轮询
如果是在slave上,或者etl&backup机器上呢? 不难发现,xtrabackup记录了redo的变化,如果我们人为的让redo不再增长,stop slave,这样的备份原理是否和xtrabackup一样呢?
这样做的好处是,不用浪费一半的空间,也不需要传送2次
你所不知道的秘密
通过以上流程图,可以发现一个重要的线索:我们的物理备份,是不会备份binlog的【重要】 所以,友谊的小船说翻就翻
对于5.1 & 5.5,用xtrabackup for 5.1 or 5.5 ,如果innodb_flush_log_at_trx_commit=1 ,应该没问题
对于5.6 , 用xtrabackup for 5.1 or 5.5 , 即便如果innodb_flush_log_at_trx_commit=1,有可能会丢失事务
对于5.6 , 用xtrabackup for 5.6, 不会丢失事务
直接copy innoDB的方式备份
好了,现在进入正题,我们是否可以类似Myisam的方式随意拷贝呢?
答案:可以
Myisam之所以可以随意拷贝,那是因为通过flush tables后,所有文件都会fsync到磁盘,这时候拷贝肯定没有问题。 InnoDB则不是,通过flush tables根本没有办法控制redo的刷新,data page的刷新以及ibdata的刷新。 所以,这里我们只要解决这三个问题即可。 redo: 我们可以通过innodb_flush_log_at_trx_commit=1来保证。 data page: 通过自身的checkpoint可以保证。 ibdata里面包含了:innodb table的数据字典信息,change buffer,doublewrite ,undo logs,这些目前好像还没有什么可以强制全部刷新。
所以,我们可以通过stop slave的方式,至少可以保证数据不丢失。至于ibdata里面的文件不刷新,也没太大关系,通过自身的crash-recover机制,自动修复。 所以,我们通过stop slave后,直接copy文件恢复的时候,你可能会看到这样的信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
2016-05-26 13:47:24 15818 [Note] InnoDB: The log sequence numbers 2446418 and 2446418 in ibdata files do not match the log sequence number 2446945 in the ib_logfiles!
2016 -05 -26 13 :47 :24 15818 [Note] InnoDB : Database was not shutdown normally!
2016 -05 -26 13 :47 :24 15818 [Note] InnoDB : Starting crash recovery.
2016 -05 -26 13 :47 :24 15818 [Note] InnoDB : Reading tablespace information from the .ibd files...
2016 -05 -26 13 :47 :24 15818 [Note] InnoDB : Restoring possible half-written data pages
2016 -05 -26 13 :47 :24 15818 [Note] InnoDB : from the doublewrite buffer...
InnoDB : Last MySQL binlog file position 0 506 , file name tjtx-101 -141.000018
2016 -05 -26 13 :47 :25 15818 [Note] InnoDB : 128 rollback segment(s) are active.
2016 -05 -26 13 :47 :25 15818 [Note] InnoDB : Waiting for purge to start
2016 -05 -26 13 :47 :26 15818 [Note] InnoDB : 5.6 .27 started; log sequence number 2446945
2016-05-26 13:47:26 15818 [Note] Recovering after a crash using /data/mysql.bin/tjtx-101-141
2016-05-26 13:47:26 15818 [Note] Starting crash recovery...
2016-05-26 13:47:26 15818 [Note] Crash recovery finished.
当你看到Crash recovery finished完成后,基本也就ok了。
好了,这里还有一个疑问我不太清楚,就是到底有多少个LSN
1
2
3
4
Log sequence number 2446945
Log flushed up to 2446945
Pages flushed up to 2446945
Last checkpoint at 2446945
那么错误日志中的The log sequence numbers xx in ibdata files ,又是啥呢?
经过测试:kill -9 mysqld 得到的结论是: 只要是非正常关闭的MySQL,都会出现这样的报错,即便是没有任何数据的MySQL 所以,我猜测,ibdata files中记录的lsn,从MySQL一启动,里面的lsn就会比redo中的lsn小,是专用于检测是否正常关闭MySQL的吗? anyway,这都不影响我们innoDB在线拷贝的大局了。
好了,通过以上测试,我们已经明白,stop slave后,等一会会,当脏页刷新完的时候,就可以认为数据全部刷新到磁盘了,这样就和Myisam没有任何区别
ok,这里又引入了一个值得思考的问题,那就是: 怎么判断脏页已经刷完了呢?
检查4个lsn,当四个lsn都相等的时候,肯定刷完了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Log sequence number 2446945
Log flushed up to 2446945
Pages flushed up to 2446945
Last checkpoint at 2446945
没错,当4 个lsn都相等的时候,的确可以表示脏页已经刷完。
但是,当4 个lsn不完全相等的时候,也有可能表示脏页刷完了。比如:
Log sequence number 2446945
Log flushed up to 2446945
Pages flushed up to 2446945
Last checkpoint at 2446747 --不一样
我来解读一下:
Last checkpoint 指的是最后一个脏页的oldest modification
Pages flushed up to 指的是最后一个脏页的newest modification
当我们再刷最后一个脏页的时候,这个脏页被多次更新,那么就表示该脏页的newest modification > oldest modification .
所以就不相等了,但是的确脏页也都刷新完了。
检查Modified db pages的值,这个值就表示flush list中还有多少个page没有被刷新,如果全为0,表示flush list中脏页全部刷新。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
假设一个实例有2 个BUFFER POOL instance,那么就要检查两个Modified db pages 是否都为0
Buffer pool size 655359
Free buffers 1000
Database pages 636541
Old database pages 234953
Modified db pages 0
Pending reads 0
Buffer pool size 655359
Free buffers 1000
Database pages 636426
Old database pages 234910
Modified db pages 0
Pending reads 0
两个都刷新完,表示flush list中已经没有脏页了。
stop slave后,如果脏页没有刷新完呢?不相等,我们还能拷贝吗? 答案是:当然可以。 lsn不相等,意味着,我们crash-recover的时候需要通过redo来恢复数据,稍微慢点而已,不影响大局。
重要
所以,不管以上两种方式的哪一种,如果你想通过stop slave的方式,直接拷贝文件的话,最好执行下
FLUSH NO_WRITE_TO_BINLOG TABLES FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS
这样,就跟xtrabackup没什么区别了
最后的几个关键问题
Xtrabackup 在什么版本恢复的时候,可能会丢失最后一个事务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Xtrabackup 2.2 .3 版本修复了此问题
https ://launchpad.net/percona-xtrabackup/2.2 /2.2 .3 -ga
Bugs Fixed
Fixed the InnoDB redo log incompatibility with 5.1 /5.5 server and compressed tables which was introduced by the upstream fix in MySQL 5.6 .11 that could make InnoDB crash on recovery when replaying redo logs created on earlier versions . Bug fixed #1255476 .
Percona XtraBackup did not flush the InnoDB REDO log buffer before finalizing the log copy. This would only become a problem when the binary log coordinates were used after restoring from a backup: the actual data files state after recovery could be inconsistent with the binary log coordinates. Bug fixed
innobackupex now sets wsrep_causal_reads to 0 before executing FLUSH TABLES WITH READ LOCK if the server is a member of the Galera cluster. Bug fixed
storage/innobase/xtrabackup/CMakeLists.txt now honors the XB_DISTRIBUTION environment variable when configuring innobackupex.pl to innobackupex. Bug fixed
When backup locks are used, xtrabackup_slave_info should be written under BINLOG lock instead of TABLE lock. Bug fixed
Other bugs fixed:
innodb 文件 和 非innodb 文件的备份,都是通过拷贝文件的方式来做的,其中有什么不同吗?
1
2
InnoDB的文件,是以page为粒度做的,xtrabackup 在读取每个page时会校验 checksum 值,保证数据块是一致的
非InnoDB 文件,是在cp MyISAM 文件时已经做了flush (FTWRL),磁盘上的文件也是完整和静止的,所以最终备份集里的数据文件都是写入完整的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
恢复的目的是把备份结果集 中的 数据恢复到一个一致性位点,所谓一致就是指原数据库某一时间点各引擎数据的状态
比如 MyISAM 中的数据对应的是 15 :00 时间点的,InnoDB 中的数据对应的是 15 :20 的,这种状态的数据就是不一致的。
PXB 备份集对应的一致点,就是备份时FTWRL的时间点,恢复出来的数据,就对应原数据库FTWRL时的状态。
因为备份时 FTWRL 后,数据库是处于只读的
非 InnoDB 数据是在持有全局读锁情况下拷贝的,所以非 InnoDB 数据本身就对应 FTWRL 时间点;
InnoDB 的 ibd 文件拷贝是在 FTWRL 前做的 , 拷贝出来的不同 ibd 文件最后更新时间点是不一样的,这种状态的 ibd 文件是不能直接用的
但是 redo log 是从备份开始一直持续拷贝的,最后的 redo 日志点是在持有 FTWRL 后取得的,所以最终通过 redo 应用后的 ibd 数据时间点也是和 FTWRL 一致的。
所以恢复过程只涉及 InnoDB 文件的恢复,非 InnoDB 数据是不动的。备份恢复完成后,就可以把数据文件拷贝到对应的目录,然后通过mysqld来启动了。
还原的流程:
* 准备(prepare)一个完全备份
innobackupex --apply-log /path/to/BACKUP-DIR : 将redo 日志apply到数据文件,让数据恢复到FTWRL的时间点
* 从一个完全备份中恢复数据
innobackupex --copy-back/move -back /path/to/BACKUP-DIR : 拷贝数据文件到MySQL目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1、先获取innodb最后一次checkpoint对应的redo log lsn;
2、xtrabackup主线程从checkpoint lsn开始将之后新产生的redo都拷贝到xtrabackup_logfile上;
3、只有拷完所有的redo日志后,才会开启redo拷贝线程和ibd拷贝线程。并行执行redo和ibd数据拷贝;
4、ibd拷贝线程完成所有数据拷贝;
5、xtrabackup执行flush table with read lock ,拷贝MyISAM表等非事务表数据;
6 、获取Binlog 文件及偏移位置,获取gtid_executed等信息;
7 、执行FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS ;
8 、等待redo拷贝进程退出;
9 、执行unlock tables ;
10 、执行备份所需其他操作后退出;
相关参考
1
2
3
http://mysql .taobao.org/monthly/2016 /03 /07 /
https://zhuanlan .zhihu.com/p/43913304
http://www .innomysql.com/xtrabackup%E5 %A4 %87 %E4 %BB %BD %E5 %8E %9F %E7 %90 %86 %E5 %AE %9E %E7 %8E %B0 %E7 %BB %86 %E8 %8A %82 -%E5 %AF %B9 %E6 %B7 %98 %E5 %AE %9D %E6 %95 %B0 %E6 %8D %AE %E5 %BA %93 %E5 %86 %85 %E6 %A0 %B8 %E6 %9C %88 %E6 %8A %A5 %E8 %A1 %A5 /