如果想知道如何使用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详解](http://keithlan.github.io/2016/04/21/transportable/) 用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文件被创建后,进行加锁
- FLUSH NO_WRITE_TO_BINLOG TABLES
- FLUSH TABLES WITH READ LOCK
- innobackupex开始检查db server支持的特性,如:gtid,backup locks,changed page bitmap等
- 然后开始拷贝非innodb文件,以及表结构frm
- 当所有的文件都备份完成后,结束redo 事务的拷贝。
接下来释放锁
- FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS
- 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的方式随意拷贝呢?
答案:可以
- 第一种方法: 保持和Myiam一致就可以。
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文件恢复的时候,你可能会看到这样的信息:
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
Log sequence number 2446945 -- redo log buffer 中的lsn,内存中记录
Log flushed up to 2446945 -- redo log file 的lsn,redo的每个lock block都会记录
Pages flushed up to 2446945 -- flush list中最后一个指针对应的page's newest modification(lsn)【last checkpoint newest modification】, 每个page的头部都会记录
Last checkpoint at 2446945 -- flush list中最后一个指针对应的page's oldest modification(lsn)【last checkpoint oldest modification】, 第一个redo的头部固定位置,存在头部的两个地方
那么错误日志中的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都相等的时候,肯定刷完了。
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中脏页全部刷新。
假设一个实例有2个BUFFER POOL instance,那么就要检查两个Modified db pages 是否都为0
---BUFFER POOL 0
Buffer pool size 655359
Free buffers 1000
Database pages 636541
Old database pages 234953
Modified db pages 0 -- 表示这个buffer pool中的脏页已经刷新完
Pending reads 0
---BUFFER POOL 1
Buffer pool size 655359
Free buffers 1000
Database pages 636426
Old database pages 234910
Modified db pages 0 -- 表示这个buffer pool中的脏页已经刷新完
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 在什么版本恢复的时候,可能会丢失最后一个事务
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 #1320685.
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 #1320441.
storage/innobase/xtrabackup/CMakeLists.txt now honors the XB_DISTRIBUTION environment variable when configuring innobackupex.pl to innobackupex. Bug fixed #1320856.
When backup locks are used, xtrabackup_slave_info should be written under BINLOG lock instead of TABLE lock. Bug fixed #1328532.
Other bugs fixed: #1318540.
- innodb 文件 和 非innodb 文件的备份,都是通过拷贝文件的方式来做的,其中有什么不同吗?
InnoDB的文件,是以page为粒度做的,xtrabackup 在读取每个page时会校验 checksum 值,保证数据块是一致的
非InnoDB文件,是在cp MyISAM 文件时已经做了flush(FTWRL),磁盘上的文件也是完整和静止的,所以最终备份集里的数据文件都是写入完整的。
- 数据恢复的原理的是什么
恢复的目的是把备份结果集 中的 数据恢复到一个一致性位点,所谓一致就是指原数据库某一时间点各引擎数据的状态
比如 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目录
- xtrabackup的详细流程细化
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、执行备份所需其他操作后退出;
相关参考
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/