MySQL备份和恢复
本文主要针对MySQL的InnoDB数据库,本文的目标:
- 对数据进行备份
- 将数据恢复到某一个时间点
原理
规划备份和恢复策略时, 可以根据一些需求来考虑:
- 恢复点目标(RPO), 指恢复到哪个时间点, 可以容许丢失多少数据
- 恢复时间目标(RTO), 指恢复可以容许的恢复时间长度
- 备份到目的地的时间, 比如本地, NFS等
备份的内容:
- MySQL配置
- schema和数据
- 二进制日志
Innodb是一个ACID系统, 任何时刻, 每个提交的事务 要么在Innodb数据文件中, 要么在二进制日志文件中。 所以为了保证一致性, 即属于同一个时间点, 备份Innodb时需要对数据和二进制日志都进行备份。
关闭MySQL备份是最简单安全的, 也是获取一致性的最好的方法, 并且损坏的风险最小. 但对于在线系统来说, 停机备份的代价太大.
如果使用了全局读锁FLUSH TABLE WITH READ LOCK, 并且备份的事务执行需要很长的时间, 则全局读锁需要等待备份的事务完成提交后才释放, 在这期间的所有操作很可能会被阻塞和积压, 即当发生一些修改操作需要等待表的写锁时, 之后的所有读写操作都会被阻塞. 而避免全局读锁的最好方法就是只使用InnoDB, 如果使用少量的MyISAM表, 则只需要锁住它们即可, 因为InnoDB支持事务, 能报保证在一个事务中的的数据的一致性。
建议混合使用物理和逻辑两种方式进行备份: 先使用物理复制, 然后启动一个测试的MySQL实例并运行mysqlcheck来检查; 然后周期性的使用mysqldump来执行逻辑备份.
备份
逻辑备份
逻辑备份只用于数据备份, 通过MySQL服务器将存储引擎中的数据导出, 与存储引擎无关, 并且可以对备份的数据进行裁剪.
缺点: 需要通过MySQL加载, 转为存储格式, 并且需要重建索引, 过程很慢, 尤其在加载一个巨大的导出文件的代价很大
需要尽量控制导出的数据文件大小,在保证一致性的前提下, 按照业务逻辑导出到多个文件中,以控制导出到粒度。
导出方式:
- 导出sql格式的schema和数据
- 导出sql格式的schema和csv格式的数据, 必须保证secure_file_priv变量不为NULL, 以及相应的路径必须存在. 需要在MySQL启动前在my.conf中配置
1 | -- 将schema导出 |
物理备份
优点:
- 物理备份的方式更加简单高效
- 恢复往往要比逻辑备份要快, 省去了加载和重建过程
缺点:
- 往往备份的大小要比逻辑备份大得多
物理备份方式:
- 基于文件拷贝的备份, 适用于关机下备份, 配置文件备份, 日志备份等
- 通过快照进行备份
快照备份
物理数据存放在/var/lib/mysql
目录下, 每个数据库存储在相同名字的目录下, 同时也包含服务器配置和二进制日志文件。
快照备份是非常好的在线备份方法, 并且可以减少持有锁的时间。 又分为:
- 最小化锁快照备份, 只锁MyISAM表,InnoDB不需要锁
- 无锁快照备份, 如果MyISAM不会发生修改, 就可以不锁表
快照备份后, 通过挂在快照, 拷贝数据文件到备份的地方。
但使用快照备份需要额外的磁盘空间, 用于快照需要的写时复制空间。 并且要求将MySQL的数据相关文件(/var/lib/mysql/)部署在同一个专有卷上。 而且会导致原始卷和快照比正常读写性能要差, 尤其在过多使用写时复制空间时。
对数据进行物理快照备份后, 需要构建一个MySQL实例并加载物理备份, 然后使用mysqlcheck可以对所有的表执行CHECK TABLES
操作, 检查物理备份的正确性.
MySQL配置备份
- /etc/mysql/my.cnf, 默认配置文件
- /etc/mysql/conf.d, 自定义配置目录, 会覆盖默认配置中的设置.
二进制日志备份
二进制日志默认存储在/var/lib/mysql/
目录下, 格式为binlog.*
, 可通过命令mysqlbinlog -d <database_name> <binlog>
来查看指定数据库的二进制日志
常用命令:
- 查看二进制日志,
show binary logs
; - 创建新的二进制日志文件,
flush logs
; - 清理指定二进制日志文件之前的日志文件,
purge binary logs to 'binlog.000005'
;
在备份数据之前, 建议flush logs生成新的日志, 可保证备份期间的操作被清楚的记录在最新的日志文件里。
最佳实践
一般情况下备份策略采用长周期的逻辑全备份(保底恢复,但加载慢) + 短周期的快照物理备份(快速恢复) + 二进制日志文件定时备份(恢复到某个时间点)。 如果数据量不大, 则只需要逻辑全备份 + 二进制日志文件备份就可以。
逻辑全备份流程:
- 在进行全备份之前, 创建新二进制日志, 保证在备份期间的操作会被记录到新二进制日志中
- 进行全备份
- 清理老的二进制日志, 只保留最新二进制日志
- 在下一次全备份之前, 定时备份二进制日志
1 | $ mysql -e "flush logs;" |
二进制日志文件备份流程:
1 | $ mysql -e "flush logs;" |
恢复
在恢复过程中, 要保证MySQL除了恢复进程外不接受其他访问, 直到恢复并检测完毕, 重新提供服务为止。
基于时间点的逻辑备份的恢复
基于时间点的恢复要求建立日常备份, 并保证所需要的二进制日志有效. 这样才能基于某一次全备份, 然后从那个时间点开始重放二进制日志, 将数据来恢复到指定时间.
基于逻辑备份恢复和二进制重放都是一个很慢的过程, 在开始恢复之前, 建议通过set sql_log_bin=0;
关闭二进制日志。
1 | # 禁用二进制日志 |
Reference
- 高性能MySQL — 第15章备份与恢复