第10章-2 备份与恢复工具
上一篇:《第10章-1 备份与恢复》,接着了解备份具体如何操作
管理和备份二进制日志
服务器的二进制日志是需要备份的最重要元素之一。它们对于基于时间点的恢复是必需的,并且通常比数据要小,所以更容易被进行频繁的备份。如果有某个时间点的数据备份和所有从那时以后的二进制日志,就可以重放从上次全备份以来的二进制日志并“向前回滚”所有的变更。
MySQL也使用二进制日志进行复制,因此备份和恢复的策略经常和复制配置相互影响。经常备份二进制日志是一个好主意。如果你不能承受丢失超过30分钟数据的代价,至少要每30分钟就备份一次。
需要制定日志的过期策略以防止磁盘被二进制日志写满。日志增长的大小取决于工作负载和日志格式(基于行的日志会产生更大的日志记录)。如果可能的话,我们建议尽可能保留日志以备所需。保留日志对于设置复制、分析服务器负载、审计和从上次全备份中按时间点进行恢复,都很有帮助。当决定要保留日志多久时,应该考虑这些需求。
一个常见的设置是使用binlog_expire_logs_seconds变量来通知MySQL定期清理日志,而不应该手动地去删除这些文件。
参数binlog_expire_logs_seconds在服务器启动或MySQL轮询二进制日志时生效,因此如果二进制日志从未填满并轮询,服务器将不会清除旧条目。MySQL服务器通过查看修改时间而不是内容来决定要清除哪些文件。
备份和恢复工具
备份工具有很多,有的很好用,有的较一般。对于裸文件备份,推荐使用Percona XtraBackup。它是开源的,被广泛使用,而且文档也非常完善。对于逻辑备份,我们首选mydumper。尽管mysqldump随MySQL一起提供,可以开箱即用,但是它的单线程特性会让备份和恢复的时间变得非常长。mydumper具有内置的并行性,可以更快地完成逻辑备份。
MySQL Enterprise Backup
这个工具是由Oracle提供的MySQL Enterprise订阅服务的一部分。使用此工具进行备份不需要停止MySQL,也不需要设置锁或中断正常的数据库活动(但是会给服务器带来一些额外的负载)。它支持类似压缩备份、增量备份和备份到其他服务器上的流备份等特性,是MySQL官方的备份工具。
Percona XtraBackup
Percona XtraBackup与MySQL Enterprise Backup在很多方面都非常类似,但它是开源并且免费的。它支持类似流、增量、压缩和多线程(并行)备份操作,它还有许多特别的功能,可以降低在高负载系统上进行备份的影响。
Percona XtraBackup的工作方式是在后台线程不断追踪InnoDB日志文件尾部,然后复制InnoDB数据文件。这是一个稍微复杂的过程,依靠特别的检测机制以确保复制的数据是一致的。当所有的数据文件被复制完,日志复制线程就结束了,结果是在不同的时间点有所有数据的副本。然后可以使用InnoDB崩溃恢复程序将日志应用到数据文件上,以达到所有数据文件有一致的状态。这一步叫作准备过程。一旦准备好,备份就会完全一致,并且包含文件复制过程最后时间点已经提交的事务。一切都在MySQL外部完成,因此不需要以任何方式连接或访问MySQL。
mydumper
几名在职和前MySQL工程师利用他们多年的经验创建了mydumper(参见链接37 GitHub - mydumper/mydumper: Official MyDumper Project),用来替代mysqldump。这是一个多线程(并发)的MySQL备份和恢复工具集,有许多很好的特性。很多人发现多线程备份和恢复的高速度是这个工具最吸引人的特色。
mysqldump
大部分人在使用这个与MySQL一起发行的程序,因此,尽管它有缺点,但在创建数据和schema的逻辑备份时最常见的选择还是mysqldump。可以参考官方手册获取更多关于如何使用该工具的细节。
数据备份
大多数时候,生成备份的方法有好的也有差的——有时候显而易见的方法并不是好方法。一个有用的技巧是应该最大化地利用网络、磁盘和CPU的能力以尽可能快地完成备份。这是一个需要不断去平衡的事情,必须通过实验找到“最佳平衡点”。
逻辑 SQL 备份
逻辑SQL导出是很多人所熟悉的,因为它们是mysqldump默认的方式。例如,用默认选项导出一个小表将产生如下(有删减)输出:
$ mysqldump test t1
-- [Version and host comments]
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
-- [More version-specific comments to save options for restore]
--
-- Table structure for table `t1`
--DROP TABLE IF EXISTS `t1`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `t1` (`a` int NOT NULL,PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `t1`
--
LOCK TABLES `t1` WRITE;
/*!40000 ALTER TABLE `t1` DISABLE KEYS */;
INSERT INTO `t1` VALUES (1);
/*!40000 ALTER TABLE `t1` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
-- [More option restoration]
导出文件包含表结构和数据,均以有效的SQL命令形式写出。文件以设置MySQL各种选项的注释开始。这些选项要么是为了使恢复工作更高效,要么是为了保证兼容性和正确性。接下来可以看到表结构,然后是数据。最后,脚本重置在导出开始时变更的选项。
导出的输出对于恢复操作来说是可执行的。这很方便,但mysqldump的默认选项对于生成一个巨大的备份却不是太适合的。
mysqldump不是生成SQL逻辑备份的唯一工具。例如,还可以用mydumper或phpMyAdmin工具来创建。我们想指出的是,不是某一个特定的工具有多大的问题,而是单独的SQL逻辑备份本身就有一些缺点。下面是几个主要的问题。
将库表结构和数据存储在一起
如果想从单个文件恢复,这样做会非常方便,但如果只想恢复一个表或只想恢复数据就很困难了。可以通过导出两次的方法来缓解这个问题——一次只导出数据,另外一次只导出schema——但还是会有下一个麻烦。
巨大的SQL语句
服务器解析和执行SQL语句的工作量非常大,所以加载数据时会非常慢。
单个巨大的文件
大部分文本编辑器都不能编辑巨大的或者包含非常长的行的文件。尽管有时候可以用命令行的流编辑器——例如,sed或grep——来抽出需要的数据,但保持文件小型化仍然是更可取的。
逻辑备份的成本很高
比起逻辑备份这种从存储引擎中读取数据后通过客户端/服务器协议发送结果集的方式,还有其他更高效的方法。
可以看到,在生产环境使用逻辑备份可能很难满足要求。如果使用逻辑备份,强烈建议考虑使用mydumper,以避免单线程备份的一些问题,并实际测试使用该工具备份对数据库的影响。
文件系统快照
文件系统快照是进行在线备份的好方法。支持快照的文件系统可以在某一时刻创建其内容的一致图像,然后你可以用它来进行备份。支持快照的文件系统和设备包括 FreeBSD 的文件系统、ZFS 文件系统、GNU/Linux 的 LVM,以及许多 SAN 系统和文件存储解决方案,比如 NetApp 存储设备。一些云提供商提供的远程附加磁盘选项也提供磁盘快照功能。
不要将快照与备份混淆。拍摄快照只是减少必须保持锁定的时间的一种方式;释放锁定后,你必须将文件复制到备份中。事实上,你甚至可以在不获取锁定的情况下选择在 InnoDB 上拍摄快照。我们将向你展示两种使用 LVM 对全 InnoDB 系统进行备份的方法,你可以选择最小或零锁定。
如果备份是用于某些特殊用户的,那么快照可能是一个非常好的方法。一个例子是在升级过程中遇到问题需要回退的情况。可以在升级前创建一个快照,这样如果升级有问题,只需要回滚到该快照。可以对任何不确定和有风险的操作都这么做,例如,对一个巨大的表做变更(需要多少时间是未知的)。
备份误区2:快照就是备份
快照,无论是LVM、ZFS还是SAN快照,都不是真正的备份,因为它们不包含数据的完整副本。因为快照是写时复制,所以它们只包含数据的实时副本与快照发生时的数据之间的差异。如果未修改的块在数据的实时副本中损坏,则没有可用于恢复的该块的完整副本,这时,每个快照都会看到与实时卷相同的损坏块。在进行备份时,可以使用快照“冻结”数据,但不要将快照本身当作备份来依赖。
LVM快照的工作原理
LVM使用写时复制(copy-on-write)的技术来创建快照——例如,对整个卷的某个瞬间的逻辑副本。这与数据库中的MVCC有点像,不同的是,LVM只保留一个老的数据版本。
注意,我们说的不是物理副本。逻辑副本看起来好像包含了创建快照时卷中所有的数据,但实际上一开始快照是不包含数据的。相比将数据复制到快照中,LVM只是简单地标记创建快照的时间点,对该快照请求读数据时,实际上是从原始卷中读取的。因此,初始的复制基本上是一个瞬间就能完成的操作,不管创建快照的卷有多大。
当原始卷中的某些数据有变化时,LVM在任何变更写入之前,都会将受影响的块复制到快照预留的区域中。LV M不保留数据的多个“老版本”,因此对原始卷中变更块的额外写入并不需要对快照做更多的其他工作。换句话说,对于每个块,只有第一次对每个块的写入才会导致将写时复制写入预留的区域。
现在,在快照中请求这些块时,LVM会从复制块而不是从原始卷中读取。所以,可以继续看到快照中相同时间点的数据而不需要阻塞任何原始卷。图10-1描述了这个方案。
图10-1:写时复制技术是如何减少快照所需空间的
快照会在/dev目录下创建一个新的逻辑卷,可以像挂载其他设备一样挂载它。从理论上讲,这种技术可以对一个非常大的卷做快照,而只需非常少的物理存储空间。但是,必须设置足够的空间,保证在快照打开时,能够保存所有期望在原始卷上更新的块。
如果不预留足够的写时复制空间,当快照用完所有的空间后,设备就会变得不可用。这个影响就像拔出一个外部设备:任何从该设备上读取数据的备份任务都会因I/O错误而失败。
先决条件和配置
创建一个快照的消耗非常小,但还是需要确保系统配置允许你获取所有备份瞬间所需文件的一致性副本。首先,确保系统满足下面这些条件。
● 所有的InnoDB文件(InnoDB的表空间文件和InnoDB的事务日志)必须都位于单个逻辑卷(分区)上。你需要绝对的时间点一致性,LVM不能为一个以上的卷做某个时间点一致的快照。(这是LVM的一个限制,其他系统没有这个问题。)
● 如果需要备份表的定义,MySQL数据目录必须在相同的逻辑卷中。如果使用另外一种方法来备份表的定义,例如,只将schema备份到版本控制系统中,就不需要担心这个问题。
● 必须在卷组中有足够的空闲空间来创建快照。需要多少取决于负载。当配置系统时,应该留一些未分配的空间以便后面做快照。
LVM有卷组的概念,它包含一个或多个逻辑卷。可以按照如下方式查看系统中的卷组:
$ vgsVG #PV #LV #SN Attr VSize VFreevg 1 4 0 wz--n- 534.18G 249.18G
输出显示了一个分布在一个物理卷上的卷组,它有4个逻辑卷,大概有250GB空闲空间。如果需要,可用vgdisplay命令产生更详细的输出。现在让我们看一下系统上的逻辑卷:
$ lvsLV VG Attr LSize Origin Snap% Move Log Copy%home vg -wi-ao 40.00Gmysql vg -wi-ao 225.00Gtmp vg -wi-ao 10.00Gvar vg -wi-ao 10.00G
输出显示mysql卷有225GB的空间。设备名是/dev/vg/mysql。这仅是一个名字,尽管看起来像一个文件系统路径。更加让人困惑的是,还有一个从同名文件到/dev/mapper/vg-mysql的设备节点的符号链接,用ls和mount命令可以观察到:
$ ls -l /dev/vg/mysql
lrwxrwxrwx 1 root root 20 Sep 19 13:08 /dev/vg/mysql -> /dev/mapper/vg-mysql
# mount | grep mysql
/dev/mapper/vg-mysql on /var/lib/mysql
有了这个信息,就可以创建文件系统快照了。
创建、挂载和删除 LVM 快照
一条命令就能创建快照。只需决定快照存放的位置和分配给写时复制的空间大小即可。不要纠结于是否使用比想象中的需求更多的空间。LVM不会马上用完所有指定的空间,只是要为后续使用预留空间而已。因此多预留一点空间并没有坏处,除非你必须同时为其他快照预留空间。
让我们来练习创建一个名为backup_mysql的快照,我们给它16GB的写时复制空间:
$ lvcreate --size 16G --snapshot --name backup_mysql /dev/vg/mysqlLogical volume "backup_mysql" created
提示
这里特意命名为backup_mysql卷而不是mysql_backup,是为了避免Tab键自动补全造成误会。这有助于避免因为Tab键自动补全导致突然误删除mysql卷组的可能。
现在让我们看看新创建的卷的状态:
$ lvsLV VG Attr LSize Origin Snap% Move Log Copy%backup_mysql vg swi-a- 16.00G mysql 0.01home vg -wi-ao 40.00Gmysql vg owi-ao 225.00Gtmp vg -wi-ao 10.00Gvar vg -wi-ao 10.00G
可以注意到,快照的属性与原始设备不同,而且该输出还显示了一些额外的信息:原始卷组和分配了16G B的写时复制空间目前已经使用了多少空间。备份时对此进行监控是一个非常好的做法,可以知道是否会因为设备写满而使备份失败。可以交互地监控设备的状态,或使用诸如Nagios这样的监控系统:
$ watch 'lvs | grep backup'
从前面mount的输出可以看到,mysql卷包含一个文件系统。这意味着快照也同样如此,可
以像其他文件系统一样挂载:
$ mkdir /tmp/backup
$ mount /dev/mapper/vg-backup_mysql /tmp/backup
$ ls -l /tmp/backup
total 188880
-rw-r-----. 1 mysql mysql 56 Jul 30 22:16 auto.cnf
-rw-r-----. 1 mysql mysql 475 Jul 30 22:31 binlog.000001
-rw-r-----. 1 mysql mysql 156 Jul 30 22:31 binlog.000002
-rw-r-----. 1 mysql mysql 32 Jul 30 22:31 binlog.index
-rw-------. 1 mysql mysql 1676 Jul 30 22:16 ca-key.pem
-rw-r--r--. 1 mysql mysql 1120 Jul 30 22:16 ca.pem
-rw-r--r--. 1 mysql mysql 1120 Jul 30 22:16 client-cert.pem
-rw-------. 1 mysql mysql 1676 Jul 30 22:16 client-key.pem
... omitted ...
这里只是为了练习,因此我们卸载这个快照并用lvremove命令将其删除:
$ umount /tmp/backup
$ rmdir /tmp/backup
$ lvremove --force /dev/vg/backup_mysqlLogical volume "backup_mysql" successfully removed
使用 LVM 快照进行无锁 InnoDB 备份
对于MySQL 8.0或者以上的版本,如果使用的都是InnoDB表,而且使用GTID和完全符合ACID的模式,那么备份就非常简单。在MySQL运行时,只需进行快照、挂载快照,然后将文件复制到备份存储中,无须锁定任何文件、捕获任何输出或执行任何特殊操作。从这些备份中进行恢复时,将执行InnoDB崩溃恢复,而GTID配置则会记录哪些事务已被处理。
为 LVM 备份做计划
规划备份最重要的事情是,为快照分配足够多的空间。我们一般采取下面的方法:
● 记住,LVM只需要将每个修改块复制到快照一次。当MySQL将一个块写到原始卷中时,它会将这个块复制到快照中,然后在异常表中为复制的块生成一个标记。后续对这个块的写操作不会产生任何复制到快照的后果。
● 如果只使用InnoDB,要考虑InnoDB是如何写数据的。InnoDB实际需要对数据写两遍,至少一半的InnoDB的写I/O会落到双写缓冲(doublewrite buffer)、日志文件,以及其他磁盘上相对小的区域中。这部分会多次重用相同的磁盘块,因此第一次写时对快照有影响,但写过一次后就不会对快照带来写压力。
● 接下来,不是反复修改同样的数据,而是要评估有多少I/O需要写入那些还没有被复制到快照写时复制空间的块,对评估的结果要保留足够的余量。
● 使用vmstat或iostat来收集服务器每秒写多少块的统计信息。
● 衡量(或评估)将备份复制到其他地方需要多久。换言之,需要在复制期间保持LVM快照打开多长时间。
假设评估出有一半的写会导致落到快照的写时复制空间的写操作,并且服务器支持10MB/s的写入。如果需要一个小时(3600s)将快照复制到另外一台服务器上,那么将需要1/2×10MB×3600即18GB的快照空间。考虑到容错,还要增加一些额外的空间。
通常,当快照保持打开时,很容易计算会有多少数据发生改变。
其他用途和替代方案
快照不仅仅用于备份,还有很多其他用途。例如,之前提到过,在一个有潜在危险的动作之前生成一个“检查点”会有帮助。有些系统允许将快照提升为原文件系统,这使得回滚到生成快照的时间点的数据非常简单。
文件系统快照不是取得数据瞬间副本的唯一方法。另外一个选择是RAID分裂:举个例子,如果你有一个三磁盘的软RAID镜像,就可以从该RAID组中移出一块磁盘单独加载。
这样做没有写时复制的代价,并且需要时将此类“快照”提升为主副本的操作也很简单。不过,天下没有免费的午餐,如果要将磁盘加回到RAID集合,就必须重新进行同步。
上一篇:《第10章-1 备份与恢复》