备份的作用
- 灾难恢复
- 回滚
- 审计
- 测试
- 创建备库
难点
- 在线备份(热备份),即不停机备份
- RTO(恢复时间目标)和 PRO(恢复点目标) 越来越严格
- FTWRL 导致的锁表
快照备份
- 快照备份是指通过文件系统支持的快照功能对数据库进行备份。
- 备份的原理是将所有的数据库文件放在同一分区中,然后对该分区执行快照工作,对于Linux而言,需要通过 LVM(Logical Volumn Manager)来实现。LVM 使用写时复制(copy-on-write)技术来创建快照,对整个卷的某个瞬间的逻辑副本。
- LVM有一个快照预留区域,如果原始卷数据有变化时,LVM保证在任何变更写入之前,会复制受影响块到快照预留区域。简单来说,快照区域内保留了快照点开始时的一致的所有old数据。对于更新很少的数据库,快照也会非常小。
- 对于MySQL而言,为了使用快照备份,需要将数据文件,日志文件都放在一个逻辑卷中,然后对该卷快照备份即可。
- 由于快照备份,只能本地,因此,如果本地的磁盘损坏,则快照也就损坏了。快照备份更偏向于对误操作防范,可以将数据库迅速恢复到快照产生的时间点,然后结合二进制日志可以恢复到指定的时间点。
Mysqldump
- 关键参数:
- –single-transaction:在开始备份前,执行start transaction命令,以此来获取一致性备份,该参数仅对innodb存储引擎有效
- –master-data=2:用于记录一致性备份的位点
- 备份的基本流程如下:
- 调用FTWRL(flush tables with read lock),全局禁止读写
- 开启快照读,获取此时的快照(仅对 InnoDB 表起作用)
- 备份非 InnoDB 表数据(.frm,.myi,*.myd等)
- 非 InnoDB 表备份完毕后,释放FTWRL锁
- 逐一备份 InnoDB 表数据
- 备份完成
- 如果在备份某个表之前,这个表做了 DDL 操作(此时备份事务暂时还没有加 MDL(元数据锁) 锁),后续再备份这个表时,就会因为表定义不一致而报错(Table definition has changed, please retry transaction)。所以总地来说,通过保存点机制,可以有效减少 DDL 操作的限制,但是也不能完全消除由于DDL操作导致的备份失败问题。
- 上图中只考虑 InnoDB 表的备份情况,实际上在unlock tables执行完毕之前,非 InnoDB 表已经备份完毕,后面的 t1,t2 和 t3 实质都是 InnoDB 表,而且 5.6 的 mysqldump 利用保存点机制,每备份完一个表就将一个表上的 MDL 锁释放,避免对一张表锁更长的时间。
Mydumper
- Mydumper原理与Mysqldump原理类似,最大的区别是引入了多线程备份(5.7.11的mysqlpump也具备并发功能),每个备份线程备份一部分表,当然并发粒度可以到行级,达到多线程备份的目的。
- 要解决最大一个问题是,如何保证备份的一致性,其实关键还是在于FTWRL。对于非innodb表,在释放锁之前,需要将表备份完成。对于innodb表,需要确保多个线程都能拿到一致性位点,这个动作同样要在持有全局锁期间完成,因为此时数据库没有读写,可以保证位点一致。
物理备份(Xtrabackup)
- 实际生产环境中我们使用的工具是 innobackupex,它是对 xtrabackup 的一层封装。innobackupex 脚本用来备份非 InnoDB 表,同时会调用 xtrabackup 命令来备份 InnoDB 表。
- innobackupex的基本流程如下:
- 开启redo日志拷贝线程,从最新的检查点开始顺序拷贝redo日志;
- 开启idb文件拷贝线程,拷贝innodb表的数据
- idb文件拷贝结束,通知调用FTWRL,获取一致性位点
- 备份非innodb表(系统表)和frm文件
- 由于此时没有新事务提交,等待redo日志拷贝完成
- 最新的redo日志拷贝完成后,相当于此时的innodb表和非innodb表数据都是最新的
- 获取binlog位点,此时数据库的状态是一致的。
- 释放锁,备份结束。
- 我们知道MySQL里面有个double-write为了防止写的时候页断裂,那么备份读的时候,有没有可能也只一半的page呢?这个其实是有可能的,因此,我们在拷贝page的时候,需要对page算checksum,如果checksum不符合预期,我们认为拷贝的页面不完整(这种情况可能发生在你在读页面,而后台线程正在刷脏),需要重试。所以,最终也能保证数据的一致性。
FLUSH TABLES WITH READ LOCK(FTWRL)
- FTWRL主要包括的 3 个步骤:
- 上全局读锁(lock_global_read_lock),为了阻止更新,获取一致性位点,但会导致所有更新操作都会被堵塞;
- 清理表缓存(close_cached_tables),也就是关闭表,这个操作对于myisam有意义,关闭myisam表时,会强制要求表的缓存落盘,这对于物理备份myisam表是有意义的,因为物理备份是直接拷贝物理文件,但如果有大查询导致关闭表等待,那么所有访问这个表的查询和更新都需要等待;
- 上全局COMMIT锁(make_global_read_lock_block_commit),主要是保证能获取一致性的binlog位点,但会堵塞活跃事务提交。
- Percona 公司对 MySQL 的 Server 层做了改进,引入了BACKUP LOCK,具体而言,通过"LOCK TABLES FOR BACKUP"命令来备份非 InnoDB 表数据;通过"LOCK BINLOG FOR BACKUP"来获取一致性位点,从而取代 FTWRL ,尽量减少因为数据库备份带来的服务受损。
- LOCK TABLES FOR BACKUP 用来备份数据
- 禁止非innodb表更新
- 禁止所有表的ddl
- 不会被大查询堵塞(关闭表)
- 不会堵塞innodb表的读取和更新,这点非常重要,对于业务表全部是innodb的情况,则备份过程中DML完全不受损
- LOCK BINLOG FOR BACKUP 用来获取一致性位点
1.禁止对位点更新的操作
2.允许DDl和更新,直到写binlog为止
参考资料
https://www.cnblogs.com/cchust/p/5452557.html
[https://www.cnblogs.com/cchust/p/4603599.html](https://www.cnblogs.com/cchust/p/4603599.html)
《高性能MySQL(第3版)》