1.快照备份
MySQL数据库本身并不支持快照功能,因此快照备份是指通过文件系统支持的快照功能对数据库进行备份。备份的前提是将所有数据库文件放在同一文件分区中,然后对该分区进行快照操作。支持快照功能的文件系统和设备包括 FreeBSD的UFS文件系统, Solaris的ZFS文件系统, GNU/Linux的逻辑管理器( Logical Volume Manager,LVM)等。这里以LVM为例进行介绍,UFS和ZFS的快照实现大致和LVM相似。
LVM是 LINUX系统下对磁盘分区进行管理的一种机制。LVM在硬盘和分区之上建立一个逻辑层,来提高磁盘分区管理的灵活性。管理员可以通过IⅥM系统轻松管理磁盘分区,例如,将若干个磁盘分区连接为一个整块的卷组( Volume Group),形成一个存储池。管理员可以在卷组上随意创建逻辑卷( Logical Volumes),并进一步在逻辑卷上创建文件系统。管理员通过LVM可以方便地调整卷组的大小,并且可以对磁盘存储按照组的方式进行命名、管理和分配。简单地说,用户可以通过LVM由物理块设备(如硬盘等)创建物理卷,由一个或多个物理卷创建卷组,最后从卷组中创建任意个逻辑卷(不超过卷组大小),如图8-1所示
图8-2显示了由多块磁盘组成的逻辑卷LV0。
通过 vgdisplay命令查看系统中有哪些卷组,如:
vgdisplay命令的输出结果显示当前系统有一个rep的卷组,大小为260.77GB,该卷组访问权限是 read/write等。命令lvdisplay可以用来查看当前系统中有哪些逻辑卷:
可以看到,一共有3个逻辑卷,都属于卷组rep,每个逻辑卷的大小都是100GB。dev/rep/repdata这个逻辑卷有两个只读快照,并且当前都是激活状态的。
LVM使用了写时复制(Copy-on- write)技术来创建快照。当创建一个快照时,仅复制原始卷中数据的元数据( meta data),并不会有数据的物理操作,因此快照的创建过程是非常快的。当快照创建完成,原始卷上有写操作时,快照会跟踪原始卷块的改变,将要改变的数据在改变之前复制到快照预留的空间里,因此这个原理的实现叫做写时复制。而对于快照的读取操作,如果读取的数据块是创建快照后没有修改过的,那么会将读操作直接重定向到原始卷上,如果要读取的是已经修改过的块,则将读取保存在快照中该块在原始卷上改变之前的数据。因此,采用写时复制机制保证了读取快照时得到的数据与快照创建时一致。
图8-3显示了LVM的快照读取,可见B区块被修改了,因此历史数据放人了快照区域。读取快照数据时,A、C、D块还是从原有卷中读取,而B块就需要从快照读取了。
命令 Ivcreate可以用来创建一个快照,--permission r表示创建的快照是只读的:
在快照制作完成后可以用 Display命令查看,输出中的 COW-table size字段表示该快照最大的空间大小, Allocated to snapshot字段表示该快照目前空间的使用状况:
可以看到,当前快照只使用0.04%的空间。快照在最初创建时总是很小,当数据来源卷的数据不断被修改时,这些数据库才会放入快照空间,这时快照的大小才会慢慢增大。
用LVM快照备份 InnoDB存储引擎表相当简单,只要把与 InnoDB存储引擎相关的文件如共享表空间、独立表空间、重做日志文件等放在同一个逻辑卷中,然后对这个逻辑卷做快照备份即可。
在对 InnoDB存储引擎文件做快照时,数据库无须关闭,即可以进行在线备份。虽然此时数据库中可能还有任务需要往磁盘上写数据,但这不会妨碍备份的正确性。因为InnoDB存储引擎是事务安全的引擎,在下次恢复时,数据库会自动检查表空间中页的状态,并决定是否应用重做日志,恢复就好像数据库被意外重启了。
2.复制
2.1 复制的工作原理
复制(replication)是MySQL数据库提供的一种高可用高性能的解决方案,一般用来建立大型的应用。总体来说, replication的工作原理分为以下3个步骤:
- 主服务器(master)把数据更改记录到二进制日志(binlog)中。
- 从服务器(save)把主服务器的二进制日志复制到自己的中继日志(relay log)中。
- 从服务器重做中继日志中的日志,把更改应用到自己的数据库上,以达到数据的最终一致性。
复制的工作原理并不复杂,其实就是一个完全备份加上二进制日志备份的还原。不同的是这个二进制日志的还原操作基本上实时在进行中。这里特别需要注意的是,复制不是完全实时地进行同步,而是异步实时。这中间存在主从服务器之间的执行延时,如果主服务器的压力很大,则可能导致主从服务器延时较大。复制的工作原理如图8-4所示。
从服务器有2个线程,一个是IO线程,负责读取主服务器的二进制日志,并将其保存为中继日志;另一个是SQL线程,复制执行中继日志。 MySQL4.0版本之前,从服务器只有1个线程,既负责读取二进制日志,又负责执行二进制日志中的SQL语句。这种方式不符合高性能的要求,目前已淘汰。因此如果查看一个从服务器的状态,应该可以看到类似如下内容:
可以看到ID为1的线程就是IO线程,目前的状态是等待主服务器发送二进制日志。ID为2的线程是SQL线程,负责读取中继日志并执行。目前的状态是已读取所有的中继日志,等待中继日志被IO线程更新。
在replication的主服务器上应该可以看到一个线程负责发送二进制日志,类似内容如下:
之前已经说过 MySQL的复制是异步实时的,并非完全的主从同步。若用户要想得知当前的延迟,可以通过命令 SHOW SLAVE STATUS和 SHOW MASTER STATUS得知,如:
通过 SHOW SLAVE STATUS命令可以观察当前复制的运行状态,一些主要的变量如下表所示。
SHOW SLAVE STATUS的主要变量
变量 | 说明 |
Slave_IO_State | 显示当前IO线程的状态,上述状态显示的是等待主服务发送二进制日志 |
Master_Log_File | 显示当前同步的主服务器的二进制日志,上述显示当前同步的是主服务器的mysql-bin.000007 |
Read_master_Log_Pos | 显示当前同步到主服务器上二进制日志的偏移量位置,单位是字节。上述的示例显示当前同步到 mysql-bin000007的551671偏移量位置,即已经同步了mysql-bin000007这个二进制日志中529MB (555176471/10241024)的内容 |
Relay_Master_Log_File | 当前中继日志同步的二进制日志 |
Relay_Log_File | 显示当前写入的中继日志 |
Relay_Log_Pos | 显示当前执行到中继日志的偏移量位置 |
Slave_IO_Running | 从服务器中IO线程的运行状态,YES表示运行正常 |
Slave_SQL_Running | 从服务器中SQL线程的运行状态,YES表示运行正常 |
Exec_master_Log_Pos | 表示同步到主服务器的二进制日志偏移量的位置。(Read_Master_Log_Pos-Exec_Master_Log._Pos)可以表示当前SQL线程运行的延时,单位是字节。上述例子显示当前主从服务器是完全同步的 |
命令 SHOW MASTER STATU可以用来查看主服务器中二进制日志的状态,如:
可以看到,当前二进制日志记录了偏移量606181078的位置,该值减去这一时间点时从服务器上的Read_Master_Log_Pos,就可以得知IO线程的延时。
对于一个优秀的 MySQL数据库复制的监控,用户不应该仅仅监控从服务器上IO线程和SQL线程运行得是否正常,同时也应该监控从服务器和主服务器之间的延迟,确保从服务器上的数据库总是尽可能地接近于主服务器上数据库的状态。
2.2 快照+复制的备份架构
复制可以用来作为备份,但功能不仅限于备份,其主要功能如下:
- 数据分布。由于 MySQL数据库提供的复制并不需要很大的带宽要求,因此可以在不同的数据中心之间实现数据的复制。
- 读取的负载平衡。通过建立多个从服务器,可将读取平均地分布到这些从服务器中,并且减少了主服务器的压力。一般通过DNS的 Round-Robin和 Linux的LVS功能都可以实现负载平衡。
- 数据库备份。复制对备份很有帮助,但是从服务器不是备份,不能完全代替备份。
- 高可用性和故障转移。通过复制建立的从服务器有助于故障转移,减少故障的停机时间和恢复时间。
可见,复制的设计不是简简单单用来备份的,并且只是用复制来进行备份是远远不够的。假设当前应用采用了主从的复制架构,从服务器作为备份。这时,一个初级DBA执行了误操作,如 DROP DATABASE或 DROP TABLE,这时从服务器也跟着运行了。
这时用户怎样从服务器进行恢复呢?
因此,一个比较好的方法是通过对从服务器上的数据库所在分区做快照,以此来避免误操作对复制造成影响。当发生主服务器上的误操作时,只需要将从服务器上的快照进行恢复,然后再根据二进制日志进行point-in-time的恢复即可。因此快照+复制的备份架构如图8-5所示。
还有一些其他的方法来调整复制,比如采用延时复制,即间歇性地开启从服务器上的同步,保证大约一小时的延时。这的确也是一个方法,只是数据库在高峰和非高峰期间每小时产生的二进制日志量是不同的,用户很难精准地控制。另外,这种方法也不能完全起到对误操作的防范作用。
此外,建议在从服务上启用read-only选项,这样能保证从服务器上的数据仅与主服务器进行同步,避免其他线程修改数据。如:
[mysqld]
read-only
在启用read-only选项后,如果操作从服务器的用户没有 SUPER权限,则对从服务器进行任何的修改操作会抛出一个错误,如: