还原:意味着从备份文件中获取数据,可以加载这些文件到mysql里,也可以将这些文件放置到mysql期望的路径中。
恢复:恢复一般意味着当某些异常发生后对一个系统或其他部分的拯救。包括从备份中还原数据,以及使服务器恢复功能的所有必要步骤,
例如,重启mysql,改变配置和预热服务器的缓存等。
1.为什么要备份
1.灾难恢复
2.人们改变想法
3.审计
4.测试
2.定义恢复需求
只有备份系统是没有用的,还需要一个强大的恢复系统。
不幸的是,让备份系统平滑工作比构造良好的恢复过程和工具更容易实现。原因如下:
1.备份在先。只有做好了备份才可能恢复。
2.备份由脚本和任务自动完成。
3.备份是日常任务,但恢复往往发生在危急情况下。
4.因为安全需要,如果正在做异地备份,可能需要对备份数据进行加密。
5.只有一个人来规划,设计和实施备份。
规划备份和恢复策略时,有2个重要的需求可以帮助思考:恢复点目标(PRO)和恢复时间目标(RTO)。它们定义了可以容忍多少数据丢失,以及需要等待
多久将数据恢复。
1.在不导致严重后果的情况下,可以容忍多少的数据丢失
2.恢复需要在多长时间内完成?哪种类型的宕机可以接受
3.需要恢复什么
3.设计MySQL备份方案
建议:
1.在生产实践中,对于大数据库而言,物理备份是必须的。逻辑备份太慢并且受到资源限制,从逻辑备份中恢复需要很长时间。基于快照的备份,例如
Percona XtraBackup 和 MySQL Enterprise Backup 是最好的选择。对于较小的数据库,逻辑备份可以很好的胜任。
2.保留多个备份集
3.定期从逻辑备份(或者物理备份)中抽取数据进行恢复测试
4.保存二进制日志以用于基于故障时间点的恢复。expire_logs_days 参数应该设置得足够长,至少可以从最近两次物理备份中做基于时间点的恢复,
这样就可以在保持主库运行且不应用任何二进制日志的情况下创建一个备库。备份二进制日志与过期设置无关,二进制日志备份需要保持足够长的时间,以便
能够从最近的逻辑备份恢复。
5.完全不借助备份工具本身来监控备份和备份的过程。需要另外验证备份是否正常
6.通过演练整个恢复过程来测试备份和恢复。测算恢复所需要的资源。
7.对安全性的考虑
弄清楚RPO和RTO可以指导备份策略。是需要机遇故障时间点的恢复能力,还是从昨晚的备份中恢复但会丢失此后的所有数据就足够了?如果需要基于时间点的恢复,
可能要建立日常备份并且保证所需的二进制日志是有效的,这样才能从备份中还原,并且通过重放二进制日志来恢复想要的时间点。
一般来说,能承受的数据丢失越多,备份越简单。如果有非常苛刻的需要,要确保能恢复所有数据,备份就能困难。基于时间点的恢复也有几类。一个'宽松'的故障
时间点恢复需求意味着需要重建数据,直到'足够接近'问题发生的时刻。一个'硬性'的需求意味着不能容忍丢失任何一个已经提交的事务。这需要特别的技术,例如将
二进制日志保存在一个独立的SAN捐或者使用DRBD磁盘复制。
1.在线备份还是离线备份
如果可能,关闭mysql做备份是最简单最安全的,也是所有获取一致性副本的方法中最好的,而且损坏或不一致的风险是最小的。如果关闭了mysql,就根本不用关心
InnoDB 缓冲池中的脏页或者其他缓存。也不需要担心数据在尝试备份的过程中被修改,并且因为服务器不对应用提供访问,所以可以更快速的完成备份。
尽管如此,让服务器停机的代价可能比看起来要昂贵。即使能最小化停机时间,在高负载和搞数据量下关闭和重启mysql也可能需要花很长一段时间。因为,必须要设计
不需要生成服务器停机的备份。即使如此,由于一致性的需要,对服务器进行的在线备份仍然会有明显的服务中断。
在众多方法中,一个最大的问题就是它们会使用 flush tables with read lock 操作。这会导致mysql关闭并锁住所有的表,将MyISAM的数据文件刷新到磁盘上,
(但InnoDB 不是这样),并且刷新查询缓存。该操作需要非常长的时间来完成。具体多长时间不可预估。如果全局读锁要等待一个长时间运行的语句完成,或者许多表,那么
时间会更长。除非锁被释放,否则就不能再服务器上更改任何数据,一切都会被阻塞和积压。flush tables with read lock 不像关闭服务器的代价那么高,因为大部分
缓存仍然在内存中,并且服务器一直是"预热"的,但是它也有非常大的破坏性。
避免使用flush tables with rad lock 的最好方法是只使用InnoDB表。在权限和其他系统信息表中使用MyISM表是不可避免的,但是如果数据改变量很少,你可以只
刷新和锁住这些表,这不会有什么问题。
在规划备份的时候,有一些与性能相关的因素需要考虑:
1.锁时间
需要持有锁多长时间,例如在备份期间持有的全局 flush tables with read lock
2.备份时间
复制备份到目的地需要多久?
3.备份负载
在复制备份到目的地时对服务器的性能的影响有多少?
4.恢复时间
把备份镜像从存储位置复制到mysql服务器,重放二进制日志等,需要多久?
最大的权衡是备份时间和备份负载,可以牺牲一个以增强另外一个。
2.物理备份还是逻辑备份
有2种主要的方法来备份mysql数据:逻辑备份(也叫'导出')和直接复制原始文件的物理备份。逻辑备份将数据包含在一种mysql能够解析的格式中,要么是sql,要么是
以某个符号分割的文本。原始文件是指存在硬盘上的文件。
逻辑备份优点:
1.逻辑备份是可以使用编辑器或者像grep和sed之类的命令查看和操作的普通文件。
2.恢复非常简单。可以通过管道把它们输入到mysql,或者使用mysqlimport
3.可以通过网络来备份和恢复。
4.可以在类似Amazon RDB 这样不能访问底层文件系统的系统中使用
5.非常灵活,因为mysqldump可以接受很多选项,例如可以使用where子句来限制需要备份哪些行。
6.与存储引擎无关
7.有助于避免数据损坏
逻辑备份的缺点:
1.必须由数据库服务器生成逻辑备份的工作,因此需要使用更多的cpu周期
2.逻辑备份在某些场景下比数据库文件本身要大。ASCII形式的数据不总是和存储引擎一样高效
3.无法保证导出后再还原出来的一定是同样的数据。浮点数问题,软件bug等
4.从逻辑备份中还原需要的mysql加载和解释语句,转化为存储格式,并重建索引,所有这一切都很慢。
最大的缺点是从mysql导出数据和通过sql语句将其加载回去的开销。如果使用逻辑备份,测试恢复需要的时间将非常重要。
物理备份优点:
1.基于文件的物理备份,只需要将需要的文件复制到其他地方即可完成备份。不需要其他额外的工作来生成原始文件。
2.物理备份的恢复可能就更简单了,这取决于存储引擎。对于MyISAM来说,只需要简单的复制文件到目的地即可。对于InnoDB则需要停止
数据库服务,可能还需要采取其他步骤。
3.InnoDB和MyISAM的物理备份非常容易跨平台,操作系统和mysql版本(逻辑备份导出亦是如此)。
4.从物理备份中恢复更快,因为mysql服务器不需要执行sql或者构建索引。如果有很大的InnoDB表,无法完全缓存到内存中,则物理备份的恢复就
快多了---至少在一个数量级。事实上,逻辑备份最可怕的地方就是不确定的还原时间。
物理备份缺点:
1.InnoDB的原始文件通常比相应的逻辑备份要大得多。InnoDB的表空间往往包含很多未使用的空间。还有很多空间被用来做存储数据以外的用途(插入缓存,回滚段等)
2.物理备份不总是可以跨平台,操作系统和MySQL版本。文件名大小敏感和浮点格式是可能会遇到麻烦。很可能因浮点格式不同而不能移动文件到另外一个系统(虽然
主流处理器都使用IEEE浮点格式)。
物理备份通常更加简单高效。尽管如此,还是需要逻辑备份。建议混合使用物理和逻辑备份:先使用物理复制,以此数据启动mysql服务器实例并运行mysqlcheck,然后
周期性的使用myqldump执行逻辑备份。这样做可以获得2种方法的优点,不会使得服务器在导出时有过度负担。如果能够方便的利用文件系统快照,也可以生成一个快照,
将该快照复制到另外一个服务器上并释放,然后测试原始文件,再执行逻辑备份。
3.备份什么
恢复的需求决定需要备份什么。最简单的策略是只备份数据和表定义。备份需要考虑的:
1.非显著数据
如二进制日志和 InnoDB事务日志
2.代码
现在mysql可以存储许多代码,如触发器和存储过程
3.复制配置
如果恢复一个涉及复制关系的服务器,应该备份所有与复制相关的文件,如二进制日志,中继日志,日志索引文件和.info文件。至少应该包含 show master status 和
show slave status 的输出。执行 flush logs 也非常有好处,可以让mysql从一个新的二进制日志开始。从日志文件的开头做基于故障时间点的恢复要比从中间容易。
4.服务器配置
假设要从一个实际的灾难中恢复,如果备份中包含服务器配置会更好。
5.选定的操作系统文件
对于服务器来说,备份中对生产服务器至关重要的任何外部配置,如cron任务,用户和组的配置,脚本管理,以及sudo规则。
增量备份和差异备份
当数据量很庞大时,一个常见的策略是做定期的增量或者差异备份。差异备份是对自上次全备份后所有改变的部分而做的备份,而增量备份则是自任意类型的上次备份后所有修改
的备份。
4.存储引擎和一致性
mysql对存储引擎的选择会导致备份更加复杂。实际上有2类一致性需要考虑:数据一致性和文件一致性。
数据一致性:
当备份时,应该考虑是否需要数据在指定的时间点一致。如果不是事务型存储引擎,则只能在备份时用 lock tables 来锁住要一起备份的表。
InnoDB 的多版本控制功能可以帮到我们。开始一个事务,转储一组相关的表,然后提交事务。(如果使用了事务获取一致性备份,则不能用lock tables,因为它会隐式的
提交事务)。只要在服务器上使用 repeatable read 事务隔离级别,并且没有任何DDL,就一定会有完美的一致性,以基于时间点的数据快照,且在备份过程中不会阻塞任何
后续的工作。
也可以使用 mysqldump 来获得 InnoDB 表的一致性逻辑备份,采用 --single-transaction 选项可以帮助按照我们所描述的那样工作。但这可能导致一个非常长的事务,
在某些负载下会导致开销不可接受。
文件一致性:
每个文件的内部一致性也非常重要。例如,一条大的update语句执行时备份反映不出文件的状态---并且所有要备份的文件互相间也应一致。如果没有内部一致的文件,还原时
可能会感到惊讶。如果是在不同的时间复制相关的文件,它们彼此可能也不一致。MyISAM 的 .MYD 和 .MYI 文件就是个例子。InnoDB 如果检测到不一致或者损坏,会记录
错误日志乃至让服务器崩溃。
对于非事务型存储引擎,比如MyISAM,可能的选项是锁住并刷新表。这意味着要么用 lock tables 和 flush tables 结合的方法以使服务器将内存中的变更刷新到磁盘上,
要么用 flush tabls with read lock。一旦刷新完成,就可以安全的复制MyISAM的原始文件。
对于InnoDB,确保文件在磁盘上一致更困难。即使使用 flush tables with rad lock, InnoDB 依旧在后台运行:插入缓存,日志和写线程继续将变更合并到日志和表空间
文件中。这些线程设计实际上是异步的---在后台执行这些工作可以帮助InnoDB取得更高的并发性---正因为如此它们与 lock tables 无关。因此,不仅需要确保每个文件内部是
一致的,还需要同时复制同一个时间点的日志和表空间。如果在备份时有其他线程在修改文件,或在与表空间文件不同的时间点备份日志文件,会在恢复后再次因系统损坏而告终。可以
通过下面几个方法来规避这个问题:
1.等到指到InnoDB的清楚线程和插入缓冲合并线程完成。可以观察show innodb status 的输出,当没有脏缓存或挂起的写时,就可以复制文件。尽管如此,这种方法可能需要
很长一段时间;因为InnoDB的后台线程设计太多的干扰而不太安全。所以不推荐。
2.在一个类似LVM的系统中获取数据和日志文件一致的快照,必须让数据和日志文件在快照时相互一致;单独取它们的快照是没有意义的。
3.发送一个 stop 信号给mysql,做备份,然后再发送一个 cont 信号来再次唤醒mysql。看起来是个很少推荐的方法,但如果另外一种方法是在备份过程中需要关闭服务器,则
这种方法值得考虑。至少这种技术不需要在重启服务器后预热。
复制:
从备库中备份的最大好处是可以不干扰主库,避免在主库上增加额外的负载。这是一个建立备库的好理由。
当从备库备份时,应该保存所有关于复制进程的信息,例如备库相对于主库的位置。这对于很多情况下都非常有用:克隆新的备库,重新应用刷新二进制日志到主库上以获取指定
时间点的恢复,将备库提升为主库等。
4.管理和备份二进制日志
服务器的二进制日志是备份的最重要因素之一。它们对于基于时间点的恢复是必须的,并且通常比数据要小,所以更容易进行频繁的备份。如果有某个时间点的数据备份和所有从那时
以后的二进制日志,就可以重放自从上次全备份以来的二进制日志并'前滚'所有的变更。
mysql 复制也使用二进制日志。因此复制和恢复的策略经常和复制配置互相影响。
为了防止二进制日志丢失,可以在不同的卷上保存数据和二进制日志。即使LVM下生成二进制日志的快照,也是可以的。为了额外的安全起见,可以将它们保存在SAN上,或用DRBD复制
到另外一个设别上。
经常备份二进制日志是个好主意。也可以用一个配置 --log_slave_update 的只读备库,这样可以获得额外的安全。备库上日志位置与主库不匹配,但找到恢复时正确的位置不难。
mysql5.6 版本的 mysqlbinlog 有一个非常方便的特性,可连接到服务器上实时对二进制日志做镜像。
1.二进制日志格式
二进制日志包含一系列的事件。每个事件有一个固定长度的头,其中包含各种信息,例如当前时间戳和默认的数据库。可以使用mysqlbinlog 工具来查看二进制日志的内容,打印出一些
头信息。下面是一个输出的例子:
#at 277
#071030 10:37 server id 3 end_log_pos 369 Qeury thread_id=13 exec_time=0 error_code=0
SET TIMESTAMP=1193755641
insert into test(a) values(2)
第一行包括日志文件内的偏移字节值(277)
第二行包含如下几项:
1.事件的日期和时间,mysql 会使用它们来产生 SET TIMESTAMP 语句
2.原服务器id,对于防止复制之间无限循环和其他问题是很有必要的
3.end_log_pos,下一个事件的偏移字节值。该值对于一个多语句事务的大部分事件是不正确的。在此类事务过程中,mysql的主库会复制事件到一个缓冲区,但这样做的时候它并不知道
下一个日志事件的位置。
4.事件类型。
5.原服务器执行事件的线程id,对于审计和执行CONNECTION_ID()函数很重要
6.exec_time,这是语句的时间戳和写入二进制日志的时间只差。不要依赖这个值,因为它可能在复制落后的备库上会有很大的差值
7.在原服务器上事件产生的错误代码。如果事件在一个备库上重放时导致不同的错误,那么复制将因安全预警而失败。
2.安全的清除老的二进制日志
需要决定日志的过期策略以防止磁盘被二进制日志写满。日志增长多大取决于负载和日志格式(基于行的日志会导致更大的日志记录)。如果有可能,建议日志有用就尽可能的保留。保留日志
对于设置复制,分析服务器负载,审计和从上次全备按时间点进行恢复,都很有帮助。
一个常见的设置是使用 expire_log_days 变量来告诉mysql定期清理日志。这个变量直到 mysql4.1才引入;在此之前,用下面cron:
0 0 * * * /usr/bin/find /var/log/mysql -mtime +N -name 'mysql-bin.[0-9]*' | xargs rm
尽管这是mysql4.1 之前清楚日志的唯一方法,但在新版本中不要这么做。用rm删除日志会导致 mysql-bin.index 状态文件与磁盘上的文件不一致,有些语句,例如 show master logs
可能会受到影响而悄然失败。手动修改 mysql-bin.index 文件也不会修复这个问题。应该用下面的 cron:
0 0 * * * /usr/bin/mysql -e "PURGE MASTER LOGS BEFORE CURRENT_DATE - INTERVAL N DAY"
expire_logs_days 设置在服务器启动或mysql切换二进制日志时生效,因此,如果二进制日志从没有增长和切换,服务器不会清楚老条目。此设置是通过查看日志的修改时间而不是内容来
来决定哪个文件需要被清楚。
5.备份数据
1.生成逻辑备份
对于逻辑备份,首先要意识到的是它们并不是以同样的方式创建的。实际上有2种类型的逻辑备份:sql导出和符号分隔文件。
sql 导出:
DROP TABLE IF EXISTS `a`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `a` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `a`
--
LOCK TABLES `a` WRITE;
/*!40000 ALTER TABLE `a` DISABLE KEYS */;
INSERT INTO `a` VALUES (1,'aaa');
/*!40000 ALTER TABLE `a` ENABLE KEYS */;
UNLOCK TABLES;
导出的文件包含表结构和数据,均以有效的sql命令形式给出。文件以设置mysql各种选项的注释开始。这些要么是为了使恢复工作更高效,要么是为了兼容性和正确性。
接下来可以看到表结构,然后是数据。最后,脚本重置在导出开始时变更的选项。
导出的输出对于还原操作来说是可执行的。这很方便,但mysqldump默认选项对于生成巨大的备份却是不合适的。
也可以使用 mydumper 或者 phpMyAdmin 工具,不是某个工具有多大问题,而是做 sql 逻辑备份本身就是有一些缺点:
1.schema 和数据存储在一起
如果想从单个文件恢复这样做很方便,但如果想恢复一个表或者恢复数据就很困难了。可以通过两次导出的方法来减缓这个问题---一次导出数据,一次只导出
schema。
2.巨大的sql语句
服务器分析和执行sql语句的工作量很大,所以加载数据时会很慢
3.单个巨大的文件
大部分文件编辑器不能编辑巨大或者包含非常长的行的文件。尽管有时候可以用命令行的流编辑器---sed或grep 抽取出需要的数据,但保持文件小型化仍然
是合适的。
4.逻辑备份的成本很高
比起逻辑备份这种从存储引擎读取数据然后通过客户端/服务器协议发送结果集的方式,还有其他更高效的方法。
这些限制意味着sql导出在表变大时可能变得不可用。不过,还有另外一个选择:导出数据到符号分隔的文件中。
符号分隔文件备份:
可以使用sql命令 select into outfile 以符号分隔文件格式创建数据的逻辑备份。(可以用mysqldump 的 --tab 选项导出符号分隔文件中)。符号分隔文件
包含以ASCII展示的原始数据,没有sql,注释和别买。
select * into outfile '/tmp/t1.txt' fields terminated by ',' optionally enclosed by '"' lines terminated by '\n' from test.t1;
比起sql导出文件,符号分隔文件更加紧凑且更易于命令工具操作,这种方法最大的优点是备份和还原速度快。可以和导出时使用一样的选项,用 load data infile
方法加载数据到表中:
load data infile '/tmp/t1.txt' into table test.t1 fields terminated by ',' optioiinally enclosed by '"' lines terminated by '\n'
但是 select into outfile 也有一些限制:
1.只能备份到运行mysql服务器的机器上的文件中。(可以自己写一个自定义的select into outfile 程序,在读取 select 结果的同时写到磁盘文件中)。
2.运行mysql的系统用户必须有文件目录的写权限,因为是由mysql服务器来执行文件的写入,而不是运行sql命令的用户
3.出于安全原因,不能覆盖已经存在的文件,不管文件的权限如何
4.不能直接导出到压缩文件中
5.某些情况下很难进行正确的导出或者导入,例如非标准的字符集。
2.文件系统快照
文件快照是一种非常好的在线备份方法。支持快照的文件系统能够在瞬间创建用来备份的内容一致的镜像。
不要把快照和备份混淆。创建快照是减少必须持有锁的时间的一种简单方法,释放锁后必须复制文件到备份中。事实上,有些时候甚至可以创建InnoDB快照而不需要锁定。
LVM是如何工作的:
LVM使用写时复制的技术来创建快照---例如,对整个卷的某个瞬间的逻辑副本。这与数据库中的MVCC有点像,不同的是它只保留一个老的数据版本。注意,我们说的
不是物理副本。逻辑副本看起来好像包含了创建快照时卷中的所有数据,但实际上一开始快照是不包含数据的。相比复制数据到快照中,LVM 只是简单的标记创建快照
的时间,然后对该快照请求数据时,实际上是从原始数据中读取的。因此,初始的复制基本上是一瞬间就能完成的操作,不管创建快照的卷有多大。
当原始卷中的某些数据变化时,LVM 在任何变更写入之前,会复制受影响的块到快照预留区域。LVM 不保留数据的多个 '老版本',因此对原始卷中变更块的额外写入
并不需要对快照做其他更多工作。换句话说,对每个块只有第一次写入才会导致写时复制到预留的区域。
现在,在快照中请求这些块时,LVM会从复制块中而不是原始卷中读取。所以,可以继续看到快照中相同时间点的数据而不需要阻塞任何原始卷。
理论上这种技术可以对一个非常大的卷做快照,而只需要非常少的物理存储空间。但是,必须设置足够的空间,保证在快照打开时,能够保存所有期望在原始卷上
更新的块。如果不保存足够的写时复制空间,当快照用完所有的空间后,设备就会变得不可用。
先决条件和配置:
创建一个快照的消耗几乎微不足道,但还是需要确保系统配置可以让你获取在备份瞬间的所有需要的文件的一致性副本。首先,要确保满足下面条件:
1.所有的InnoDB文件(InnoDB的表空间文件和InnoDB的事务日志)必须是在单个逻辑卷(分区)。你需要绝对的时间点一致性,LVM不能为多于一个卷做某个时间
一致的快照。
2.如果需要备份表定义,mysql数据目录必须在相同的逻辑卷中。
3.必须在卷组中有足够的空闲空间来创建快照。
用于在线备份的LVM快照:
如何在不停止MySQL服务器的情况下备份InnoDB数据库,这里需要一个全局的读锁。连接mysql服务器并使用一个全局读锁将表刷到磁盘上,然后获取二进制日志
的位置:
flush tables with read lock; show master status;
记录 show master status 的输出,确保mysql的连接处于打开状态,以使读锁不被释放。然后获取 LVM 快照并立即释放该读锁,可以使用 unlock tables
或者直接关闭连接来释放锁。
文件系统快照和InnoDB:
即使锁住所有的表,InnoDB的后台线程仍然会继续工作。因此,即使在创建快照时,仍然可以往文件中写入。并且,由于InnoDB没有执行关闭操作,如果服务器
意外断电,快照中InnoDB的文件会和服务器意外掉电后文件的遭遇一样。
这不是什么问题,因为InnoDB是个ACID系统。任何时刻,每个提交的事务要么在InnoDB数据文件中要么在日志文件中。在还原快照后启动mysql时,InnoDB将
运行恢复进程,就像服务器断电一样。它会查找事务日志中任何提交但没有应用到数据文件中的事务然后应用,因此不会丢失任何事务。这正是要强制InnoDB数据
文件和日志文件再一起快照的原因。
使用LVM快照无所InnoDB备份:
无锁备份只有一点不同。区别是不需要执行 flush tables with read lock。这意味着不能保证MyISAM文件再磁盘上一致,如果只使用InnoDB,这就不是问题。
规划LVM备份:
LVM快照备份也是有开销的。服务器写到原始卷的越多,引发的额外开销也越多。当服务器随机修改许多不同块时,磁头需要自写时复制来来回回寻址,并且将数据的
老版本写到写时复制空间。从快照中读也有开销,因为LVM需要从原始卷中读取大部分数据。只有快照创建后修改过的数据从写时复制空间读取;因此,逻辑顺序读取
快照数据实际上也可能导致磁头来回移动。
因此应该为此规划好快照。快照实际上会导致原始卷和快照比正常的读/写性能都要差---如果使用过多的写时复制空间,性能可能会更差。这会降低mysql服务器和
复制文件进行备份的性能。
规划中另外一个重要的事情是,为快照分配足够多的空间。一般采取下面方法:
1.记住,LVM只需要复制每个修改块到快照一次。mysql写一个块到原始卷中时,它会复制这个块到快照中,然后对复制的块在例外表中生成一个标记,后续对这个
块的写不会产生任何到快照的复制。
2.如果只使用InnoDB,要考虑InnoDB是如何写数据的。InnoDB实际需要对数据写2遍,至少一半的InnoDB的写IO会到双写缓冲,日志文件,以及其他磁盘上相对
小的区域中。这部分会多次重用相同的磁盘块,因此第一次时对快照有影响,但写过一次以后就不会对快照带来任何压力。
3.接下来,相对于反复修改同样的数据,需要评估有多少IO需要写入到那些还没有复制到快照写时复制的块中,对评估的结果要保留足够的余量。
4.使用vmstat和iostat来收集服务器每秒写多少块的统计信息
5.衡量复制备份到其他地方需要多久。
备份误区2:'快照就是备份':
一个快照,不论是LVM快照,ZFS快照,还是SAN快照,都不是实际的备份,因为它不包含数据的完成副本。正因为快照是写时复制,所以它只包含实际数据和快照
发生的时间点的数据之间的差异数据。如果一个没有被修改的块在备份副本时被损坏,那就没有该块的正常副本可以用来恢复,并且备份副本时每个快照看到的都是
相同的损坏的块。可以使用快照来'冻结'备份时的数据,但不要把快照当做一个备份。
快照的其他用途和替代方案:
还有其他用途,例如,在一个潜在的危险动作之前,生成一个'检查点'。
文件系统快照不是取得数据瞬间副本的唯一方法。另外一个选择是RAID分裂。
6.从备份中恢复
如何恢复取决于是怎么备份的。可能需要以下部分或者全部步骤:
1.停止mysql服务器
2.记录服务器的配置和文件权限
3.将数据从备份中移到mysql数据目录
4.改变配置
5.改变文件权限
6.以限制访问模式重启服务器,等待完成启动
7.载入逻辑备份文件
8.检查和重放二进制日志
9.检测已经还原的数据
10.以完全权限重启服务器
在恢复过程中,保证mysql除了恢复进程以外不接受其他访问,这一点往往更重要。我们喜欢以 --skip-networking 和 --socket=/tmp/mysql_recover.sock 选项
来启动mysql,以确保它对于已经存在的应用不可访问,直到我们检测完并重新提供服务。这对于按块加载的逻辑备份尤为重要。
1.恢复物理备份
恢复物理备份往往非常直接,一般过程是简单的复制文件到正确位置。
是否需要关闭mysql取决于存储引擎。MyISAM的文件一般互相独立,即使服务器正在运行,简单的复制每个表的 .frm, .MYI, .MYD 文件也可以正常的操作。一旦有
任何对此表的查询,或者其他会导致服务器访问此表的操作(如 show tables),mysql会立刻找到这些表。如果在复制这些文件时表是打开的,可能会遇到麻烦,因此,
操作前要么删除或者重命名表,要么使用 lock tables 和 flush tables 来关闭它。
InnoDB 的情况有所不同。如果用传统的InnoDB的步骤来还原,即所有的表都存储在单个表空间,就必须关闭mysql,复制或移动文件到正确的位置上,然后重启。
同样也需要innodb的事务日志文件和表空间文件匹配。如果文件不匹配---例如,替换了表空间文件但没有替换事务日志文件---innodb将会拒绝启动。这也是将日志和数据
文件一起备份非常关键的一个原因。
如果使用innodb的 file-per-table 特性(innodb_file_per_table),innodb 会将每个表的数据和索引存储于一个 .ibd 文件中,这就像 MyISAM的 .MYI和.MYD
文件合一起。可以在服务器运行时通过复制这些文件来备份和还原单个表,但这并不像MyISAM中那样简单。这些文件并不完全独立于innodb。每个.ibd 文件都有一些内部的信息。
保存着它与主(共享)表空间之间的关系。在还原这样的文件时,需要让innodb先'导入'这个文件。
这个过程有许多限制,最大的限制可能是只能在当初备份的服务器上还原单个表。
还原物理备份后启动mysql:
在启动正在恢复的mysql服务器之前,还有些步骤要做:是在启动mysql之前检查服务器的配置,确保恢复的文件有正确的归属和权限。这些属性必须正确,否则mysql可能
无法启动。这些属性因系统的不同而不同。一般都需要mysql用户和组拥有这些文件和目录,并且拥有可读/写权限。
建议观察mysql启动时的错误日志。老版本在innodb有错时不会启动,新版本不管怎么样都会启动,而只是让innodb失效。即使服务器看起来没有任何问题,也应该对每个
数据库运行 show table status 来再次检查错误日志。
2.还原逻辑备份
如果还原的是逻辑备份而不是物理备份,则与使用操作系统简单的复制文件到适当的位置的方式不同,需要使用mysql服务器本身来加载数据到表中。
在加载导出文件之前,应该花点时间思考文件有多大,需要多久加载完,以及在启动之前还需要做什么。服务器加载一个巨大的导出文件的代价很高。禁掉二进制日志是个好主意。
加载巨大的文件对一些存储引擎也有影响。例如,在单个事务中加载100G数据到innodb就不是个好办法,因为巨大的回滚段将会导致问题。应该以控制大小的块来加载,并逐个提交事务。
加载sql文件:
如果有一个sql导出文件,它将包含可执行的sql。需要做的就是运行这个文件。
mysql < sakila-backup.sql
也可以从mysql命令行客户端用source 命令加载文件。这只是做相同事情的不同方法。
mysql> set sql_log_bin=0;
mysql> source sakila-backup.sql;
mysql> set sql_log_bin=1;
需要注意的是,如果使用 source,当定性文件到mysql时,默认情况下,发生一个错误不会导致一批语句的错误。
如果备份做过压缩,那么不要分别解压和加载。应该在单个操作中完成解压和加载。这样做会快很多。
gunzip -c sakila-backup.sql.gz | mysql
如果只想恢复单个表,怎么做?
grep 'insert into `actor`' sakila-backup.sql | mysql sakila
或者,如果是压缩过的文件,那么命令是:
gunzip -c sakila-backup.sql.gz | grep 'insert into `actor`' | mysql sakila
加载分隔符文件:
如果是通过 select into outfile 导出的符号分隔文件,可以使用 load data infile 通过相同的参数来加载。也可以使用mysqlimport,这是load data infile
的包装。这种方式依赖命名约定决定从哪里加载一个文件的数据。
使用 load data infile 有一个非常好的优化技巧。load data infile 必须直接从文本文件中读取,因此,如果是压缩文件很多人会在加载前先解压,这是非常慢的磁盘
密集型的操作。然后,在支持 FIFO '命名管道' 文件的系统上,对这种操作有个好的办法。首先是创建一个命名管道并解压数据流到它里面。
mkfifo /tmp/backup/payment.fifo
chmod 666 /tmp/backup/payment.fifo
gunzip -c /tmp/backup/payment.txt.gz > /tmp/backup/payment.fifo
注意我们使用了一个大于符号来重定向解压输出到 payment.fifo 文件中 --- 而不是在不同的程序之间创建匿名的管道符号。
管道会等待,直到其他程序打开头并从另外一头读取数据。mysql服务器可以从管道中读取解压后的数据,就像从其他文件一样。如果可能,禁用二进制日志。
set sql_log_bin=0; -- Optional
load data infile '/tmp/backup/payment.fifo'
into table sakila.payment
一旦mysql加载完数据,gunzip 就会退出,然后就可以删除该命令管道。在mysql命令行客户端使用source命令加载压缩的文件也可以使用此技术。
3.基于时间点的恢复
对mysql做基于时间点的恢复的常见方法是还原最近一次的全备份,然后从那个时间点开始重放二进制日志(有时叫向前恢复)。只要有二进制日志,就可以恢复
到任何希望的时间点。甚至可以不费力气的恢复单个数据库。
主要的缺点是二进制日志重放可能会是一个很慢的过程。它大体上等于复制。如果有一个备库,并且已经测量到sql线程的利用率有多高,那么对重放二进制日志
有多快大概心里有数了。例如,如果sql的利用率只有50%,则恢复一周二进制日志的工作可能在三到四天内完成。
一个典型的场景是对有害的结果做回滚。例如,drop table 操作。看只有MyISAM 表的情况下该如何做。假如是在半夜,备份任务在运行与下面的列相当的语句,
复制数据库到同一服务器的其他地方。
mysql> flush tables with read lock;
-> server1# cp -a /var/lib/mysql/sakila /backup/sakila;
mysql> flush logs;
-> server1# mysql -e "show master status" --vertical > /backup/master.info;
mysql> unlock tables;
假设晚些时候,有人运行了下面的语句:
use sakila;
drop table sakila.payment;
首先,停止mysql以阻止更多的修改,然后从备份中仅恢复sakila数据库。
server1# /etc/init.d/mysql stop
server1# mv /var/lib/mysql/sakila /var/lib/mysql/sakila.tmp
server1# cp -a /backup/sakila /var/lib/mysql
再到运行的服务器的 my.conf 中添加如下配置以禁止正常的连接
skip-networking
socket=/tmp/mysql_recover.sock
现在可以安全的启动mysql了
server1# /etc/init.d/mysql start
下一个任务是从二进制日志中分出需要重放和忽略的语句。事发时,自半夜的备份以来,服务器只创建了一个二进制日志。我们可以用 grep 来检查二进制日志文件
以找到问题语句。
server1# mysqlbinlog --database=sakila /var/log/mysql/mysql-bin.00215 | grep -B3 -i 'drop table sakila.payment'
#at 352
#070919 16:11:00 server id 1 end_log_pos 429 ...
可以看到,我们想忽略的语句在日志文件中的 352 位置,下一个语句位置是 429。可以用下面的命令重放日志到 352位置,然后从 429 开始:
server1# mysqlbinlog --database=sakila /var/log/mysql/mysql-bin.00215
--stop-position=352 | mysql -u root -p
server1# mysqlbinlog --database=sakila /var/log/mysql/mysql-bin.00215 --start-position=429 | mysql -u root -p
接下来要做的是检查数据以确保没有问题,然后关闭服务器并撤销对 my.conf 的改变,最后重启服务器。
4.更高级的恢复技术
复制和基于时间点的恢复使用的是相同的技术:服务器的二进制日志。这意味着复制在恢复时会是个很好的工具,哪怕方式不是很明显。
用于快速恢复的延时复制:
如果有一个延时的备库,并且在备库执行问题语句之前就发现了问题,那么基于时间点的恢复就更快更容易了。
停止备库,用 start slave until 来重放事件直到要执行问题语句。接着,执行 set global sql_slave_skip_counter=1 来跳过
问题语句。如果想跳过多个事件,可以设置一个大于1的值(或者简答的使用 change master to 来前移备库在日志中的位置)。
然后要做的就是执行 start slave,让备库执行完所有的中继日志。这样就利用备库完成了基于时间点的恢复中所有冗长的工作。现在可以将
备库提升为主库,整个恢复过程基本上没有中断服务。
使用日志服务器进行恢复:
还有另外一种使用复制来做恢复的方法:设置日志服务器。我们感觉复制比mysqlbinlog更可靠,mysqlbinlog 可能会有一些导致行为异常的奇怪
bug 和不常见的情况。使用日志服务器进行恢复比 mysqlbinlog 更灵活简单,不仅因为 start slave until 选项,还因为那些可以采用的复制
规则(例如 replicate-do-table)。使用日志服务器,相对于其他方式来说,可以做到更复杂的过滤。
误操作 drop table ,现在想恢复此操作,但又不想让服务器退到昨晚的备份,下面是利用日志服务器进行恢复的步骤:
1.将需要恢复的服务器叫做 server1
2.在另外一台叫做 server2 的服务器上恢复昨晚的备份。在这台服务器上运行恢复进程,以免在恢复时犯错而导致事情更糟。
3.按照第10章的做法设置日志服务器来接收 server1 的二进制日志
4.改变server2的配置文件,增加如下内容:
replicate-do-table=sakila.payment
5.重启server2,然后利用 change master to 来让它成为日志服务器的备库。配置它从昨晚备份的二进制日志坐标读取。这时候切记不要运行start slave
6.检测server2 上的 show slave start 输出,验证一切正常
7.找到二进制日志中问题语句的位置,在server2上执行 start slave until 来重放事件直到该位置
8.在server2 上用 stop slave 停掉恢复进程。现在应该有被删除的表,因为现在从库停止在被删除之前的时间点
9.将所需表从 server2 复制到 server1
只有在没有多表的 update,delete 或 insert 语句操作这个表时,上述流程才是可行的。
5.InnoDB崩溃恢复
InnoDB 在每次启动时都会检测数据和日志文件,以确认是否需要执行恢复过程。而且,InnoDB的恢复过程与之前讨论的不是一回事。它并不是恢复备份的数据,而是根据
日志文件将事务应用到数据文件,将未提交的变更从数据文件中回滚。
大部分情况下InnoDB可以很好的解决问题。除非mysql有bug或者硬件问题,否则不需要做任何非常规的事情,哪怕是服务器意外断电。InnoDB 会在启动时执行正常的恢复,
然后就一切正常了。在日志文件中,可以看到如下信息:
InnoDB: Doing recovery :scanned up to log sequence number
InnoDB: Starting an apply batch of log records ...
InnoDB 会在日志文件中输出恢复进度的百分比信息。有些人说直到整个过程完成才能看到这些信息。耐心点,这个恢复过程是急不来的。如果心急而杀掉进程并重启,只会
导致更长的恢复时间。
InnoDB 损坏的原因:
InnoDB 非常健壮且可靠,并且有许多内建的安全来检测防止,检测和修复损坏的数据。
最起码,innodb 依赖于无缓存io调用和 fsync() 调用,直到数据完全写入到物理介质才会返回。如果硬件不能保证写入的持久化,innodb也就不能保证数据的持久,
崩溃就有可能导致数据损坏。
很多innodb损坏问题都是与硬件有关。错误配置的硬件是更多的问题之源。常见的错误配置包含了 打开不包含电池备份单元的 RAID 卡的会写缓存,或打开了硬盘驱动器
本身的回写缓存。这些错误将会导致控制器或驱动器'说谎',在数据实际上只写入到回写缓存上而不是磁盘上,却说 fsync() 已经完成。换句话说,硬件没有提供保持innodb
数据安全的保证。
有时候机器默认就会这样配置,因为会得到更好的性能---但对事物数据服务来说却是个很大的问题。
如果在网络附加存储(NAS)上运行innodb,也可能会遇到损坏,因为对NAS设备来说完成 fsync() 只是意味着设备接收到了数据。如果innodb崩溃,数据是安全的,但如果
NAS 设备崩溃就不一定了。
如何恢复损坏的InnoDB数据:
innodb 损坏主要有3种类型:
1.二级索引损坏
一般可以用 optimize table 来修复损坏的二级索引;另外,也可以用 select into outfile,删除和重建表,然后 load data infile 的方法。(也可以将
表改为使用MyISAM 再改回来)。这些过程都是通过构建一个新表重建受影响的索引,来修复损坏的索引数据。
2.聚簇索引损坏
如果是聚簇索引损坏,也许只能使用 innodb_force_recovery 选项来导出表。有时导出过程会让 innodb 崩溃,如果出现这样的情况,或许跳过导致崩溃的损坏页
以导出其他的记录。聚簇索引的损坏比二级索引要更难修复,因为它会影响数据行本身,但在多数场合下仍然只需要修复受影响的表。
3.损坏系统结构
系统结构包括innodb事务日志,表空间的回撤日志(undo log)区域和数据字典。这种损坏可能需要做整个数据库的导出和还原。因为innodb内部绝大部分的工作都可能
受到影响。
一般可以修复损坏的二级索引而不丢失数据。然后,另外两种情形经常会引起数据的丢失。如果已经备份,那最好还是从备份中还原,而不是试着从损坏的文件中提取数据。
如果必须从损坏的文件中提取数据,那一般的过程是先尝试让innodb运行起来。然后使用 select into outfile 导出数据。如果服务器已经崩溃,并且每次启动innodb
都会崩溃,那么可以配置innodb停止常规恢复和后台进程的运行。
innodb_force_recovery 参数控制着innodb在启动和常规操作时要做哪一种类型的操作。通常情况下这个值是0,可以增大到6.在有点危险的情况下,可以把这个值调到
4.使用这个设置,若有数据损坏,将会丢失一些数据。如果将数据设高,可能会从损坏页里提取到损坏的数据,或者增加执行select into outfiles 时崩溃的风险。
换句话说,这个值到4都对数据没有损坏,但可能丧失恢复问题的机会;而5到6会更主动的修复,但损坏数据的风险也会更大。
当把 innodb_force_recovery 设置为大于0的某个值时,innodb 基本上是只读的。但仍然可以创建和删除表。这样可以阻止进一步的损坏,innodb 会放松一些常规
检查,以便发现坏数据时不会特意崩溃。
7.备份和恢复工具
1.MySQL ENterprise Backup
2.Percona XtraBackup
3.mylvmbackup
4.Zmanda Recovery Manager
5.mydumper
6.mysqldump
尽管有些缺点,但创建数据和schema的逻辑备份最常见的选择还是 mysqldump。这是一个通用工具,可以用于许多的任务,例如
//在服务器间复制表。
mysqldump --host=server1 test t1 | mysql --host=server2 test;
//对服务器上的所有内容创建逻辑备份到单个文件中,每个库中所有的表在相同逻辑时间点备份
mysqldump --all-databases > dump.sql
//创建只包含sakila示例数据库的逻辑备份
mysqldump --databases sakila > dump.sql
//创建只包含 sakila.actor表的逻辑备份
mysqldump sakila actor > dump.sql
//可以使用 --result-file 选项来指定文件,防止在windows上发生换行符转换
mysqldump sakila actor --result-file=dump.sql
常见选项:
--opt
启用一组优化选项,包括关闭缓冲区(它会使服务器耗尽内存),导出数据时把更多的数据写在更少的sql语句里,以便在加载的时候更加有效率,以及做其他一些有用的
事情。如果关闭了这组选项,mysqldump 会在把表写到磁盘之前,把他们都导出到内存里,这对于大型表而言是不切实际的。
--allow-keywords, --quete-names
使用户在导出和恢复表时,可以使用保留字作为表的名字
--complete-insert
使用户能在不完全相同列的表之间移动数据
--tz-utc
使用户在具有不同时区的服务器之间移动数据
--lock-all-tables
使用 flush table with read lock 来获取全局一致的备份
--tab
用 select into outfile 导出文件
--skip-extended-insert
使每一行数据都有 insert 语句。它的代价是使得文件更大,导入mysql的开销更大。
如果在mysqldump上使用 --databases 或者 --all-databases 选项,那么最终导出的数据在每个数据库中都一致,因为mysqldump会在同一时锁定并导出一个数据库里
所有的表。然后,来自不同数据库的各个表就未必是互相一致的。使用 --lock-all-tables 选项可以解决这个问题。
对于innodb备份,应该增加 --single-transaction 选项,这会使用 innodb 的 mvcc 特性在单个时间点创建一个一致的备份,而不需要使用 lock tables 锁定所有的
表。如果增加 --master-data 选项,备份还会包含在备份时服务器的二进制日志文件位置,这对于基于时间点的恢复和设置恢复非常有帮助。然后也要知道,获取日志位置时需要
使用 flush tables with read lock 冻结服务器。
8.备份脚本化
为备份写一些脚本是标准的做法。
1.安全监测
打开严格的错误监测,并且使用英文名
use strict;
use warnings FATAL => 'all'
use English qw(-no_match_vars)
Base 下,下面会让 有未定义的变量或持续出错退出时产生一个错误
set -u;
set -e;
2.启动mysql
建议用系统的标准发放
3.获取数据库和列表
show databases;
show tables from db;
show table status from db;
4.对表加锁,刷新并解锁
lock tables 表名 read
flush tables;
flush tables 表名
flush tables with read lock;
unlock tables;
5.刷新二进制日志
flush logs;
6.获取二进制日志
show master status;
show slave status;