MariaDB备份恢复②:LVM快照 + 二进制日志

2018-05-23|Categories: Database|

创建分区

查看现有磁盘分区:

$ parted /dev/sda unit s print free
Model: VMware, VMware Virtual S (scsi)
Disk /dev/sda: 419430400s
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start      End         Size        Type     File system  Flags
        63s        2047s       1985s                Free Space
 1      2048s      411647s     409600s     primary  xfs          boot
 2      411648s    46565375s   46153728s   primary               lvm
        46565376s  419430399s  372865024s           Free Space

创建新分区,并设置lvm标记:

parted --script /dev/sda            \
    mkpart primary 46565376s 50G    \
    set 3 lvm on
  • 因为使用了--script非交互模式,所以通过精确的扇区数指定新分区的起始位置,避免parted询问扇区数导致分区失败。
  • 加上lvm标记仅仅是一种提醒,后续必须要使用相关命令才可以在这个分区上创建逻辑卷。

分区结果如下:

$ parted /dev/sda print
Model: VMware, VMware Virtual S (scsi)
Disk /dev/sda: 215GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      1049kB  211MB   210MB   primary  xfs          boot
 2      211MB   23.8GB  23.6GB  primary               lvm
 3      23.8GB  50.0GB  26.2GB  primary               lvm

确保内核已经识别新分区:

$ lsblk /dev/sda
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda               8:0    0  200G  0 disk
├─sda1            8:1    0  200M  0 part /boot
├─sda2            8:2    0   22G  0 part
│ ├─centos-root 253:0    0   20G  0 lvm  /
│ └─centos-swap 253:1    0    2G  0 lvm  [SWAP]
└─sda3            8:3    0 24.4G  0 part

如果没有看到sda3分区,需要手工检测新分区并通知内核:

  • CentOS 7:执行partprobe命令。
  • CentOS 6:执行partx -a /dev/sda命令。

创建并挂载LVM

创建逻辑卷:

pvcreate /dev/sda3

vgcreate vg_db /dev/sda3

lvcreate -n lv_db -L 5G vg_db
lvcreate -n lv_binlog -L 5G vg_db

格式化逻辑卷:

mkfs.xfs /dev/vg_db/lv_db
mkfs.xfs /dev/vg_db/lv_binlog

挂载逻辑卷:

mkdir /db_backups/{data,binlog} -pv

mount /dev/vg_db/lv_db /db_backups/data/
mount /dev/vg_db/lv_binlog /db_backups/binlog/

挂载结果:

$ lsblk /dev/sda3 -o +fstype
NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT         FSTYPE
sda3                8:3    0 24.4G  0 part                    LVM2_member
├─vg_db-lv_db     253:2    0    5G  0 lvm  /db_backups/data   xfs
└─vg_db-lv_binlog 253:3    0    5G  0 lvm  /db_backups/binlog xfs

更改目录的所有者、所属组,让MariaDB对这些目录有写入权限:

$ chown -R mysql. /db_backups/{data,binlog}

$ ls -ld /db_backups/{data,binlog}
drwxr-xr-x 2 mysql mysql 6 May 23 10:03 /db_backups/binlog/
drwxr-xr-x 2 mysql mysql 6 May 23 10:03 /db_backups/data/

把MariaDB的数据、日志存储到LVM

vim /etc/my.cnf

修改[mysqld]配置段:

[mysqld]
datadir = /db_backups/data
log_bin = /db_backups/binlog/mariadb-bin

重启服务:

systemctl restart mysqld

我是二进制安装MariaDB server,启动脚本名为/etc/init.d/mysqld

如果MariaDB是通过yum安装,服务名称应该是mariadb

Yum安装的MariaDB在重启过程中会自动在新的datadir生成相关文件,例如mysql这个最重要的数据库的目录。但我通过二进制安装的MariaDB却重启失败,检查错误日志发现如下内容:

$ less /db_backups/data/vm71.err
# ...
[ERROR] Fatal error: Can't open and lock privilege tables: Table 'mysql.user' doesn't exist

于是我从修改前的datadir路径把mysqlperformance_schema两个目录移动到新的datadir

cp -a /data/mariadb/mysql/ /db_backups/data/
cp -a /data/mariadb/performance_schema/ /db_backups/data/

再次重启,成功。

创建测试数据库

因为之前重启失败,导致生成了多个二进制日志:

MariaDB [(none)]> show master logs;
+--------------------+-----------+
| Log_name           | File_size |
+--------------------+-----------+
| mariadb-bin.000001 |       330 |
| mariadb-bin.000002 |       330 |
| mariadb-bin.000003 |       330 |
| mariadb-bin.000004 |       330 |
+--------------------+-----------+
4 rows in set (0.00 sec)

这些日志没有包含任何有用的信息,反倒会给后续实验造成干扰,于是我用reset master;重新设置二进制日志的状态:

MariaDB [(none)]> reset master;
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> show master logs;
+--------------------+-----------+
| Log_name           | File_size |
+--------------------+-----------+
| mariadb-bin.000001 |       330 |
+--------------------+-----------+
1 row in set (0.00 sec)

这里仍然使用来自《SQL必知必会》这本书的测试数据库tysql

# 下载
mkdir -p /src/tysql && cd $_
wget http://forta.com/books/0672336073/TeachYourselfSQL_MySQL.zip

# 解压
unzip TeachYourselfSQL_MySQL.zip

# 转换文件格式
dos2unix *.txt

# 创建数据库
mysql -e 'create database tysql;'

# 导入第一个文件(创建空表)
mysql tysql < create.txt

# 导入第二个文件(填充刚创建的表)
mysql tysql < populate.txt

tysql数据库包含了五张表:

MariaDB [tysql]> show tables;
+-----------------+
| Tables_in_tysql |
+-----------------+
| Customers       |
| OrderItems      |
| Orders          |
| Products        |
| Vendors         |
+-----------------+
5 rows in set (0.00 sec)

其中的Orders表包含了五条记录:

MariaDB [tysql]> select * from Orders;
+-----------+---------------------+------------+
| order_num | order_date          | cust_id    |
+-----------+---------------------+------------+
|     20005 | 2012-05-01 00:00:00 | 1000000001 |
|     20006 | 2012-01-12 00:00:00 | 1000000003 |
|     20007 | 2012-01-30 00:00:00 | 1000000004 |
|     20008 | 2012-02-03 00:00:00 | 1000000005 |
|     20009 | 2012-02-08 00:00:00 | 1000000001 |
+-----------+---------------------+------------+
5 rows in set (0.00 sec)

我在这张表插入一条新记录,作为恢复数据库之后的核对标记:

insert into Orders values (20010, now(), '1000000002');

再次查询,结果如下:

MariaDB [tysql]> select * from Orders;
+-----------+---------------------+------------+
| order_num | order_date          | cust_id    |
+-----------+---------------------+------------+
|     20005 | 2012-05-01 00:00:00 | 1000000001 |
|     20006 | 2012-01-12 00:00:00 | 1000000003 |
|     20007 | 2012-01-30 00:00:00 | 1000000004 |
|     20008 | 2012-02-03 00:00:00 | 1000000005 |
|     20009 | 2012-02-08 00:00:00 | 1000000001 |
|     20010 | 2018-05-23 14:06:25 | 1000000002 |
+-----------+---------------------+------------+
6 rows in set (0.00 sec)

给数据库创建快照

数据库全局加读锁

flush tables with read lock;

记录二进制日志的位置

mkdir /binlog_bak

$ mysql -uroot -p -e 'show master logs;' | tee /binlog_bak/$(date +%F-%H%M%S).binlog
Enter password:
Log_name    File_size
mariadb-bin.000001  16403

给存放数据的LVM创建快照

因为数据目录/db_backups/data/dev/vg_db/lv_db这个逻辑卷的挂载点,换句话说,数据库文件都存放在/dev/vg_db/lv_db这个逻辑卷上,因此我们就给这个逻辑卷做快照:

$ lvcreate -s -n lv_db_snap -L 1G -p r /dev/vg_db/lv_db
  Using default stripesize 64.00 KiB.
  Logical volume "lv_db_snap" created.

测试数据库很小,只有122M,所以这里给快照指定1G空间已经绰绰有余了:

$ du -sm /db_backups/data
122   /db_backups/data

查看快照的详细信息,LV snapshot status字段表明这个快照的源就是lv_db

$ lvdisplay /dev/vg_db/lv_db_snap
  --- Logical volume ---
  LV Path                /dev/vg_db/lv_db_snap
  LV Name                lv_db_snap
  VG Name                vg_db
  LV UUID                MAwMgD-5Mzs-vbh2-BRDe-0MrW-PbSg-0oxO6b
  LV Write Access        read only
  LV Creation host, time vm71, 2018-05-23 14:28:10 +0800
  LV snapshot status     active destination for lv_db
  LV Status              available
  # open                 0
  LV Size                5.00 GiB
  Current LE             1280
  COW-table size         1.00 GiB
  COW-table LE           256
  Allocated to snapshot  0.00%
  Snapshot chunk size    4.00 KiB
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     8192
  Block device           253:6

恢复数据库的正常访问

flush logs;

unlock tables;

从LVM快照中备份数据库

挂载快照

mkdir /mnt/lv_snap

mount -o nouuid,norecovery /dev/vg_db/lv_db_snap /mnt/lv_snap

这里必须使用nouuid选项才可以挂载成功,因为逻辑卷的文件系统是xfs,挂载时需要用UUID来识别设备的唯一性,而快照的UUID和它的源逻辑卷完全相同,会导致挂载失败:

$ blkid | grep lv_db
/dev/mapper/vg_db-lv_db: UUID="d2ef3617-6e07-46a4-a9fc-3171b4614f79" TYPE="xfs"
/dev/mapper/vg_db-lv_db_snap: UUID="d2ef3617-6e07-46a4-a9fc-3171b4614f79" TYPE="xfs"

另外,创建快照时通过-p r指定了只读属性,所以还要使用norecovery选项:

$ man mount

nouuid    Don't check for double mounted file  systems  using  the  file
          system  uuid.   This  is useful to mount LVM snapshot volumes,
          and often used in combination with "norecovery"  for  mounting
          read-only snapshots.

打包备份数据库文件

mkdir /data

tar czvf /data/full_bak_$(date +%F-%H%M%S).tar.gz -C /mnt/lv_snap/ .

注意,打包命令的末尾-C /mnt/lv_snap/ .有一个点号.,表示打包时用点号.替换/mnt/lv_snap

最终生成的文件是:

$ ls -lh /data/full_bak_2018-05-23-*
-rw-r--r-- 1 root root 757K May 23 15:24 /data/full_bak_2018-05-23-152422.tar.gz

删除快照

备份完成后,要立即删除快照,因为源逻辑卷中的文件只要被修改,都要先把拍快照那一刻的文件从源逻辑卷复制到快照,这会明显降低数据库的写入效率。

首先取消挂载:

umount /mnt/lv_snap/

然后删除快照:

$ lvremove /dev/vg_db/lv_db_snap
Do you really want to remove active logical volume vg_db/lv_db_snap? [y/n]: y
  Logical volume "lv_db_snap" successfully removed

模拟删库

systemctl stop mysqld

/bin/rm -rf /db_backups/data/*

恢复数据库

tar xvf /data/full_bak_2018-05-23-152422.tar.gz -C /db_backups/data

如果备份后、删库前还有其它修改数据库的操作,需要拿备份的二进制日志的位置和最新的对比,然后把备份后产生的日志用mysqlbinlog导出为SQL文件,适当修改后导入数据库,恢复到删库前的状态,并检查恢复数据的状态。详细步骤参见《MariaDB备份恢复①:mysqldump + 二进制日志》,此处不再赘述。

总结

通过LVM快照备份数据库的最大优势是制作快照「速度快」,对用户正常访问的影响较小,但整个操作还包括单独备份二进制日志位置、挂载快照、从快照中打包数据、取消挂载快照、删除快照等步骤,相对比较繁琐。

Leave A Comment