上一篇:《第10章-2 备份与恢复工具》,接着了解常用的工具Percona
Percona XtraBackup
XtraBackup是备份MySQL最流行的解决方案之一,这是有充分理由的。它支持非常灵活的配置,包括备份压缩、文件加密等。
XtraBackup 的工作原理
InnoDB是一个崩溃安全的存储引擎。如果MySQL发生崩溃,它会使用基于重做日志(redo log)的崩溃恢复模式来恢复正确的数据。Percona XtraBackup也是基于这种设计的。当使用Percona XtraBackup进行备份时,它会记录日志序列号(LSN),并使用该序列号对备份文件执行崩溃恢复。它还会在一些特定的时刻锁定实例,以确保与复制相关的信息准确无误。更详细的说明,请参阅XtraBackup 文档(可参见链接38 https://oreil.ly/8JWIB)。
这是一个XtraBackup备份的示例:
$ xtrabackup --backup --target-dir=/backups/
xtrabackup version 8.0.25-17 based on MySQL server 8.0.25 Linux (x86_64)
(revision id: d27028b)
Using server version 8.0.25-15
210821 17:01:40 Executing LOCK TABLES FOR BACKUP…
至此,我们可以看到,XtraBackup已经确定了MySQL的运行版本。这可以帮助它确定针对这个版本应该支持哪些功能以及如何备份文件。比如,在这个例子中,可以使用命令LOCK TABLES FOR BACKUP来获取表锁:
XtraBackup正在将文件从源复制到目的地:
210821 17:01:42 Finished backing up non-InnoDB tables and files
210821 17:01:42 Executing FLUSH NO_WRITE_TO_BINLOG BINARY LOGS
210821 17:01:42 Selecting LSN and binary log position from p_s.log_status
210821 17:01:42 [00] Copying /var/lib/mysql/binlog.40 to /backups/binlog.04
up to position 156
210821 17:01:42 [00] ...done
210821 17:01:42 [00] Writing /backups/binlog.index
210821 17:01:42 [00] ...done
210821 17:01:42 [00] Writing /backups/xtrabackup_binlog_info
210821 17:01:42 [00] ...done
完成文件复制后,它会收集与复制相关的信息:
210821 17:01:42 Executing FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS...
xtrabackup: The latest check point (for incremental): '35005805'
xtrabackup: Stopping log copying thread at LSN 35005815.
210821 17:01:42 >> log scanned up to (35005825)
Starting to parse redo log at lsn = 35005460
210821 17:01:43 Executing UNLOCK TABLES
210821 17:01:43 All tables unlocked
现在,XtraBackup已经确定了InnoDB的最新检查点。这将有助于它应用备份期间发生的写入日志。它使用UNLOCK TABLES释放先前LOCK TABLES FOR BACKUP获取的锁:
210821 17:01:43 [00] Copying ib_buffer_pool to /backups/ib_buffer_pool
210821 17:01:43 [00] ...done
210821 17:01:43 Backup created in directory '/backups/'
MySQL binlog position: filename 'binlog.000004', position '156'
210821 17:01:43 [00] Writing /backups/backup-my.cnf
210821 17:01:43 [00] ...done
210821 17:01:43 [00] Writing /backups/xtrabackup_info
210821 17:01:43 [00] ...done
xtrabackup: Transaction log of lsn (35005795) to (35005835) was copied.
210821 17:01:44 completed OK!
最后一步是记录LSN,导出缓存池中的页面信息,并将其写入最终文件。另外,还有my.cnf文件的副本,xtrabackup_info文件则包含有关备份的元数据,如MySQL UUID、服务器和XtraBackup的版本信息。
示例用法
我们将重点介绍一些XtraBackup的常见用法,但在此之前有一些事项需要注意:
● 在安装MySQL的时候应正确配置密码保护。所以,这里也需要确保使用--user和--password选项指定具有足够权限的账户来进行备份。
● XtraBackup的输出也很冗长。在示例中,删除了部分输出,以突出每个用例中最重要的部分。
● 此时依旧建议,在运行任何命令之前,都先查看一下Percona XtraBackup的官方手册,因为语法和选项可能会发生变化。虽然我们还没有发现任何与该工具相关的数据丢失问题,但也还是建议你在正式上线使用之前,先对非生产环境进行相关的测试与验证。
将数据备份到某个目录
第一个示例展示了如何使用XtraBackup将数据完整备份到另一个目录中。之后,你再考虑如何处理数据,这里的目录可以是另一块磁盘上的目录,也可以是同一块磁盘上的目录,或更大的备份服务器上挂载的共享文件目录。需要注意,因为是进行全量备份,所以需要有足够的空间来存储备份文件。
这是XtraBackup最基本的用法,指定模式(--backup)和备份文件的位置(--target-dir):
$ xtrabackup --backup --target-dir=/backups/
执行后,输出将类似于本章前面“XtraBackup的工作原理”小节中的输出。如果成功,/backups目录将包含数据的完整副本。
流式备份
将所有文件复制到新目录可能不是最理想的用例。通常,会在一个目录中保存多个备份。这时可以使用流备份选项(--stream)。这种方式可以将备份写为单个文件:
$ xtrabackup --backup --stream=xbstream > /backups/backup.xbstream
在这种用法中,我们仍然指定以backup模式运行,并删除了target-dir选项,因为输出将是标准输出(STDOUT)。然后,再将标准输出重定向到一个文件就可以了。
请注意,还可以使用带有日期的Bash shell命令在目标文件名中包含时间戳,如下所示:
$ xtrabackup --backup --stream=xbstream > /backups/backup-$(date +%F).xbstream
在整个备份过程中运行方式不变,<STDOUT>始终都是备份的目标端。备份文件将被写入/backups中的xbstream文件。
压缩备份
正如我们之前提到的,目标端需要有足够的空间来存储数据文件的完整副本,或者需要有足够的空间来存放单个xbstream文件。减少空间需求的一种常见方法是使用XtraBackup的压缩功能:
$ xtrabackup --backup --compress --stream=xbstream > /backups/backup-
compressed.xbstream
你会注意到,以上命令在执行过程中输出的信息不再是“Streaming”而是“Compressing and streaming”。在这个测试中,加载了Sakila样本数据库,并观察到一个原本是94MB的未压缩xbstream文件变成了一个6.5MB的压缩文件。
使用加密备份
要介绍的最后一个示例是在备份中使用加密。加密会使用更多的CPU资源,备份过程会花费更长的时间;但是,考虑到备份很容易通过一个文件获取大量数据,所以作为折中方案还是可以使用加密的。在这里,运行参数依旧覆盖了备份模式、流式传输,但额外使用密码加密encrypt和encrypt-key-file来指向密钥所在的位置:
$ xtrabackup --backup --encrypt=AES256 --encrypt-key-
file=/safe/key/location/encrypt.key --stream=xbstream > /backups/backup-
encrypted.xbstream
在执行过程中,命令输出的每个备份文件的信息都变成了“Encrypting and streaming”。请注意,也可以使用参数--encrypt-key在命令行中指定密钥。但最好不要这样做,否则,密钥将在进程列表中,或作为Linux上/proc文件系统的一部分,被公开展示。
其他重要标志
通常,我们会关注备份需要多长时间。为了加快备份速度,可以查看并尝试--parallel和-compress-threads选项。使用这些选项会增加CPU使用率,但会减少备份所需的总时间。加密也有类似的并行化选项。
如果你有大量的数据库和表,可以使用--rsync来优化文件复制过程。
从备份中恢复
如何恢复数据取决于数据是怎么备份的。可能需要以下部分或全部步骤。
1. 停止MySQL服务器。
2. 记录服务器的配置和文件权限。
3. 将数据从备份中移到MySQL数据目录。
4. 改变配置。
5. 改变文件权限。
6. 以限制访问模式重启服务器,等待其完成启动。
7. 载入逻辑备份文件。
8. 检查和重放二进制日志。
9. 检测已经还原的数据。
10. 以完全权限重启服务器。
我们将在接下来的章节中演示这些步骤的具体操作,还会对本节及本章后面几节提及的一些特殊的备份方法和工具做一些解释。
如果有机会使用文件的当前版本,就不要用备份中的文件来代替。例如,如果备份包含二进制日志,并且需要重放这些日志来做基于时间点的恢复,那么不要把当前二进制日志用备份中的老的副本替代。如果有需要,可以将其重命名或移动到其他地方。
在恢复过程中,需要确保MySQL除了恢复进程外不接受其他访问,这一点往往比较重要。我们首选以--skip-networking和--socket=/tmp/mysql_recover.sock选项来启动MySQL,以确保它对于已经存在的应用不可访问,直到检测完并重新提供服务。这对于按块加载的逻辑备份的恢复来说尤其重要。
恢复逻辑备份
如果恢复的是逻辑备份而不是裸文件备份,则与使用操作系统将文件简单地复制到适当位置的方式不同,需要使用MySQL服务器本身来将数据加载到表中。
在加载导出文件之前,应该先花一点时间考虑文件有多大,需要多久加载完,以及在启动之前还需要做什么事情,例如,通知用户或禁掉部分应用。禁掉二进制日志也是一个好主意,除非需要将还原操作复制到副本:服务器加载一个巨大的导出文件的代价很高,并且写二进制日志会增加更多的(可能没有必要的)开销。加载巨大的文件对于一些存储引擎也有影响。例如,在单个事务中将100GB数据加载到InnoDB就不是一个好想法,因为巨大的回滚段将会导致问题。应该以可控大小的块来加载,并且逐个提交事务。有两种类型的逻辑备份,所以相应地有两种类型的恢复操作。
如果有一个SQL导出文件,它包含可执行的SQL语句。需要做的就是运行这个文件。假设将Sakila示例数据库和schema备份到单个文件,下面是用来恢复的常用命令:
$ mysql < sakila-backup.sql
也可以从mysql命令行客户端用SOURCE命令加载文件。这只是做相同事情的不同方法,不过该方法可使得某些事情变得更简单。例如,如果你是MySQL管理用户,那么可以关闭在客户端连接中执行语句的二进制记录,然后加载文件,而不需要重启MySQL服务器:
SET SQL_LOG_BIN = 0;
SOURCE sakila-backup.sql;
SET SQL_LOG_BIN = 1;
需要注意的是,如果使用SOURCE,当将文件重定向到mysql时,在默认情况下,会发生一个错误,但不会导致一批语句退出。
如果对备份做过压缩,那么不要分别解压缩和加载,应该在单个操作中完成解压缩和加载。这样做会快很多:
$ gunzip -c sakila-backup.sql.gz | mysql
如果只想恢复单个表(例如,actor表),要怎么做呢?如果数据没有分行但有schema信
息,那么恢复数据并不难:
$ grep 'INSERT INTO `actor`' sakila-backup.sql | mysql sakila
或者,如果文件是被压缩过的,那么命令如下:
$ gunzip -c sakila-backup.sql.gz | grep 'INSERT INTO `actor`'| mysql sakila
如果需要创建表并恢复数据,而在单个文件中有整个数据库,则必须先编辑这个文件。这也是有一些人喜欢将每个表导出到各自文件中的原因。大部分编辑器无法应付巨大的文件,尤其如果文件是被压缩过的。另外,你也不会想真正地去编辑文件本身——而只想抽取相关的行——因此可能必须做一些命令行工作。使用grep仅抽出给定表的INSERT语句较简单,就像我们在前面命令中做的那样,但得到CREATE TABLE语句比较难。下面是抽取所需段落的sed脚本:
$ sed -e '/./{H;$!d;}' -e 'x;/CREATE TABLE `actor`/!d;q' sakila-backup.sql
不得不承认,这条命令非常晦涩。如果你必须以这种方式恢复数据,那只能说明备份设计得非常糟糕。如果有所规划,可能就不需要痛苦地去尝试弄清楚sed如何工作了。只需要将每个表备份到各自的文件,或者可以更进一步,分别备份数据和schema。
从快照中恢复原始文件
恢复裸文件备份往往非常直接,换言之,没有太多的选项。这可能是好事,也可能是坏事,具体取决于恢复的需求。一般过程是简单地将文件复制到正确位置。
如果InnoDB的配置方式是比较古老的,比如使用了单表空间来存储所有的表,那么就必须关闭MySQL,将文件复制或移动到正确位置,然后重启。同时还需要确保InnoDB的事务日志文件与表空间文件相匹配。如果文件不匹配——例如,替换了表空间文件但没有替换事务日志文件,InnoDB将无法启动。这也是将日志和数据文件一起备份非常关键的一个原因。
如果使用InnoDB的file-per-table特性(innodb_file_per_table),InnoDB会将每个表的数据和索引存储于一个.ibd文件中。可以在服务器运行时通过复制这些文件来备份和还原单个表,但也并不是那么简单。这些文件并不完全独立于InnoDB。每个.ibd文件都有一些内部的信息,保存着它与主(共享)表空间之间的关系。在还原这样的文件时,需要让InnoDB先“导入”这个文件。
这个过程有许多限制,如果有需要可以阅读MySQL用户手册中关于每个表使用独立表空间的部分。最大的限制是,只能在当初备份的服务器上还原单个表。用这种配置来备份和还原多个表不是不可能,但可能比想象中要更棘手。
所有这些复杂度意味着恢复裸文件备份会非常乏味,并且容易出错。一个值得倡导的规则是,恢复过程越难越复杂,也就越需要逻辑备份的保护。为了防止一些无法预料的情况,或者应付某些无法使用裸文件备份的场景,准备好逻辑备份总是值得推荐的。
使用 Percona XtraBackup 进行恢复
在本章前面“XtraBackup的工作原理”一节中,我们提到XtraBackup使用InnoDB的崩溃恢复过程来进行安全备份。这意味着在使用XtraBackup备份的文件之前,还需要执行额外的步骤。
如果使用流式备份,则需要先解压缩xbstream文件。对于xbstream,可以使用xbstream命令提取:
$ xbstream -x < backup.xbstream
这个命令会将所有文件提取到当前的位置,也可用-C选项预先指定特定的目录。如果使用了压缩或加密选项,还需要使用类似的选项来反转该过程。对于压缩文件,使用--decompress,对于加密过的文件,在指定--encrypt-key-file位置时使用-decrypt选项:
$ xbstream -x --decompress < backup-compressed.xbstream
$ xbstream -x --decrypt --encrypt-key-file=/safe/key/location/encrypt.key
< backup-encrypted.xbstream
接下来,是准备文件。准备的过程是实际执行崩溃恢复操作并确保你正在恢复所有数据的过程:
$ xtrabackup --prepare --target-dir=/restore
提示
如果你没有使用流模式,则可以在备份后立刻执行准备阶段。存储已经完成准备操作的备份文件,在后续需要恢复时,这会大大缩短恢复时间。
一旦完成并成功,就可以立即使用这些文件来启动MySQL:
$ xtrabackup --move-back --target-dir=/restore
提示
可以将参数--copy-back或--move-back与xtrabackup一起使用,以将文件复制或移动到正确的位置。
XtraBackup将自动从MySQL配置中检测你的data-dir变量,并将文件移动到正确的位置。
在恢复原始文件后启动 MySQL
在启动正在恢复的MySQL服务器之前,还有一些步骤要做。
首先,最重要且最容易忘记的事情是,在启动MySQL服务器之前检查服务器的配置,确保恢复的文件有正确的归属和权限。这些属性必须完全正确,否则MySQL可能无法启动。这些属性因系统的不同而不同,因此要仔细检查是否和之前做的记录吻合。一般需要mysql用户和组拥有这些文件和目录,并且只有这个用户和组拥有读/写权限。
建议观察MySQL启动时的错误日志。在类UNIX系统上,可以这样观察文件:
$ tail -f /var/log/mysql/mysql.err
注意,错误日志的准确位置会有所不同。一旦开始监测文件,就可以启动MySQL服务器并监测错误。如果一切进展顺利,MySQL启动后就有一个恢复好的数据库服务器了。
观察错误日志对于新的MySQL版本更为重要。老版本在InnoDB有错时不会启动,但新版本不管怎样都会启动,只是会让InnoDB失效。即使服务器的启动看起来没有任何问题,那也应该对每个数据库运行SHOW TABLE STATUS来再次检测错误日志。
总结
每个人都知道需要备份,但并不是每个人都能意识到需要的是可恢复的备份。有许多方法可以规划能满足恢复需求的备份。为了避免出现问题,我们建议你要明确并记录恢复点目标和恢复时间目标,并且在选择备份系统时将其作为参考。
在日常基础上做恢复测试以确保备份可以正常工作也很重要。设置mysqldump并让它在每天晚上运行是很简单的,但很多时候你没有意识到数据随着时间已经增长到可能只有几天或几周才能再次导入的地步。最糟糕的是,当你真正需要恢复的时候,才发现原来需要这么长时间。毫不夸张地说,一个在几个小时内完成的备份可能需要几周时间来恢复,具体取决于硬件、schema、索引和数据。
不要掉进副本就是备份的陷阱。副本对生成备份而言是一个干涉较少的源,但它不是备份本身。对于RAID卷、SAN和文件系统快照,道理也一样。确保备份可以通过DROP TABLE测试(或“遭受黑客攻击”的测试),还要能通过数据中心失败的测试。如果是基于备库生成备份,需要通过从源重建备份,并从那时起强制执行super_read_only来确保你的所有副本是一致的。
毫无疑问,我们推荐的备份方式是使用Percona XtraBackup进行裸文件备份,或使用mydumper进行逻辑备份。
这两种方法都可以无侵入地实现二进制的原始数据备份,这样的备份可以通过启动mysqld实例检查所有的表来进行验证。有时候甚至可以一石二鸟:在开发或者预开发环境中每天对裸文件备份进行恢复测试,然后再将数据导出为逻辑备份。我们也建议备份二进制日志,并且尽可能久地保留多份备份的数据和二进制文件。这样即使最近的备份无法使用,还可以使用较老的备份来执行恢复或者创建新的副本。
上一篇:《第10章-2 备份与恢复工具》