对Mongodb的备份可以选择很多不同的方式,在这里主要讨论使用LVM快照对Mongodb做备份的方式。

LVM快照简介

LVM的快照本质上就是为LV创建一个快照区,这个快照区本身也是一个逻辑卷,用来存放数据块修改之前的数据。它采用COW(Copy On Write)的方式进行快照数据的管理,即快照LV创建的时候,它只创建一个指向源数据的硬链接,并不会真正的复制数据,原LV的数据发生变化的时候,先将写入前的数据镜像一份到快照区,然后在进行数据的修改。通过这种方式,就能确保通过快照区读取的数据都是在创建快照那一刹那的状态。

Mongodb_备份还原_LVM快照备份_MONGODB

在上图中,左边的一幅图片表示为原本的LV创建了一个快照区,此时快照区里面并没有LV的数据,而仅仅是一堆指向原LV的硬链接。当对数据块A发生修改的时候,首先将数据块A原来的样子做一份镜像复制到快照区,然后在进行修改。在修改完成之后,虽然原LV的数据发生了变化,但是通过快照区读取的数据还是创建快照那一刹那的状态,因为对于修改过的数据块A来说,快照记录的是修改前的状态,对于没有修改的数据块来说,快照区通过硬链接也能读取到原来的数据。

根据以上的原理,能得出两个猜想:

  • 如果原来的LV损坏,那么快照也将损坏
  • 如果修改的量太大,快照区的大小不足以存放那些已经修改的数据,那么快照会失效(损坏)


基于LVM快照对Mongo的备份还原

备份过程中会丢数据吗?

因为LVM的快照备份是基于文件系统层面的备份,也就是意味着没有写入磁盘的数据是不会被备份的。对于WiredTiger的存储引擎来说,数据文件里面的数据只是反映了最后一次检查点的状态,那么内存中的修改就无法反映到快照中,但是在开启了journal的情况下,这些数据并不会丢失,因为在journal中已经反映了这些修改,通过读取journal的数据就能重放这些修改。个人认为在创建快照过程本质上就是类似于Oracle的instance recovery的过程,能确保写到journal中的数据都能被恢复在快照完成之后快照的数据就处于一个相对静止的状态(参考LVM快照简介)

常规备份恢复

备份

mongodb配置参数如下

[root@vm002 ~]# cat /etc/mongod_ts.conf 
storage:
dbPath: "/mg_data/data/"
systemLog:
destination: file
path: "/mg_data/log/mongod.log"
logAppend: true
net:
port: "27037"
bindIp: localhost,vm002
processManagement:
fork: true

a) 创建快照

[root@vm002 ~]# lvcreate --size 200M --snapshot --name mg_sp01  /dev/mongo_data/mg_data_new
Logical volume "mg_sp01" created.

到目前为止,创建了一个200M的快照,名字为mg_sp01


b) 对快照进行归档

[root@vm002 ~]# dd if=/dev/mongo_data/mg_sp01 | gzip > mybk_abc_0602.gz
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 10.1931 s, 105 MB/s

在创建快照之后, /dev/mongo_data/mg_data_new这个LV的任何变化都会将修改之前的数据同步一份到快照区,如果不对快照进行归档,那么终究会有快照被撑满的一天,这个时候快照数据就无法使用。在快照数据被归档之后,快照区既可清理。

c) 模拟数据变更

> db.abc.find().count();
5
> db.abc.insert({a:1});
db.abc.insert({a:1});
WriteResult({ "nInserted" : 1 })
> db.abc.insert({a:1});
WriteResult({ "nInserted" : 1 })
> db.abc.insert({a:1});
WriteResult({ "nInserted" : 1 })
> db.abc.insert({a:1});
WriteResult({ "nInserted" : 1 })
>

新的数据变更不会体系到快照里面

数据恢复

假设因为某些原因需对对数据进行恢复

破坏原来的数据
[root@vm002 ~]# dd if=/dev/zero of=/dev/mongo_data/mg_data_newbs=1m count=100

使用如下方式对数据进行恢复

--创建LV
[root@vm002 ~]# lvcreate -L 1g -n mg_data1 mongo_data

--从原来的压缩文件中恢复数据
[root@vm002 ~]# gzip -d -c mybk_abc_0602.gz | dd of=/dev/mongo_data/mg_data1
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 36.3198 s, 29.6 MB/s

[root@vm002 ~]# mount /dev/mongo_data/mg_data1 /mg_data/

确认数据

> use mydb
switched to db mydb
> db.abc.find().count();
5
>可以发现,还是只有原来的5条数据,新的数据是没有被快照数据记录的


几个猜想

不归档快照会怎样

快照区中对于没有修改的数据块只有一个硬链接,也就意味着,如果原来的文件损坏,快照的数据也就损坏,不对快照数据做归档,是没有任何备份意义的。

--破坏原来的LV
[root@vm002 ~]# dd if=/dev/zero of=/dev/mongo_data/mg_data_new
dd: writing to ‘/dev/mongo_data/mg_data_new’: No space left on device
2097153+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 36.6198 s, 29.3 MB/s

--这个时候对快照的读取就会报错
[root@vm002 ~]# dd if=/dev/mongo_data/mg_sp01 | gzip > mybk_abc_0602_2.gz
dd: error reading ‘/dev/mongo_data/mg_sp01’: Input/output error
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.000112413 s, 0.0 kB/s

所以说,千万不要认为做了快照就是备份,需要将快照数据归档到一个安全的地方才是完成了一个备份。