本文标签: mongodb , mongodb备份恢复方法 , 大数据 , 大数据技术
数据定期备份是非常有必要的,数据的重要性不言而喻,地球上的猪都会知道。
mongodb备份方式有三种,下面分别说到:
1. 文件快照方式
2. 复制数据文件方式
3. 使用mongodump方式
这是最简单的备份方法。但是,需要系统文件支持快照和mongod必须启用journal。如果都符合这两条要求,可以在任何时刻创建快照。
恢复时,确保没有运行mongod,执行快照恢复操作命令,然后启动mongod进程,mongod将重放journal日志。
直接拷贝数据目录下的一切文件。但是在拷贝过程中必须阻止数据文件发生更改。因此需要对数据库加锁,以防止数据写入。
1
|
> db.fsyncLock() |
上面的命令将阻塞写入操作,并将脏数据刷新到磁盘上,确保数据一致。
然后,拷贝数据文件到备份目录下
1
|
# cp -R /data/db/* /backup |
文件复制完成后,对数据库进行解锁,允许写操作
1
|
> db.fsyncUnlock() |
注意: 在执行db.fsyncLock()和db.fsyncUnlock()时,不能关闭当前的shell窗口,否则可能无法连接而需要重新启动mongod服务。
恢复时,确保mongod没有运行,清空数据目录,将备份的数据拷贝到数据目录下,然后启动mongod
1
2
|
# cp -R /backup/* /data/db/ # mongod -f mongod.conf |
千万不要fsyncLock与mongodump配合使用,如果数据库被锁定了,mongodump将永远挂起。
使用mongodump备份比较慢,在备份复制集时还有些问题,后续会说到。但是,用来备份单个数据库、集合、子集合还是比较好的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
# ./mongodump --help options: --help 显示帮助信息 -v [ --verbose ] 打印出更多信息,如时间等等 -vvvvv --version 打印版本信息 -h [ --host ] arg 指定连接的mongodb主机,复制集时设置为< set name>/s1,s2 --port arg 指定mongodb端口号,也可以这么指定--host hostname:port --ipv6 启用支持IPv6 support -u [ --username ] arg 用户名 -p [ --password ] arg 密码 --authenticationDatabase arg user source (defaults to dbname) --authenticationMechanism arg (=MONGODB-CR) authentication mechanism --dbpath arg 直接访问mongod的数据库文件,而不是连接到mongodb服务器。需要锁定数据目录,如果mongod当前在访问相同路径将不能使用。也就是说,mongod运行的情况下不能使用--dbpath,mongod未运行的情况下可以直接指定--dbpath --directoryperdb 每个db一个单独的目录,需要指定dbpath --journal 启用journaling,需要指定dbpath -d [ --db ] arg 指定数据库 -c [ --collection ] arg 指定集合 -o [ --out ] arg (=dump) 指定输出目录, "-" 表示标准输出 -q [ --query ] arg json查询 --oplog 使用oplog来生产时间点快照 --repair 尝试恢复崩溃的数据库 --forceTableScan 强制表扫描,不使用$snapshot |
1
|
# mongodump -p 27017 |
将在当前目录下创建dump目录,备份所有的数据库,所有的数据存储在.bson文件中,可以使用mongodb提供的bsondump工具来检索它。
mongod未运行情况下:
1
|
# mongodump --dbpath /data/db |
恢复时,使用mongorestore工具恢复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# ./mongorestore --help //相同部分参数意义参加上面的mongodump -v [ --verbose ] --version -h [ --host ] arg --port arg --ipv6 -u [ --username ] arg -p [ --password ] arg --authenticationDatabase arg --authenticationMechanism arg (=MONGODB-CR) --dbpath arg --directoryperdb --journal -d [ --db ] arg -c [ --collection ] arg --objcheck 在插入前验证对象,默认启用 --noobjcheck 不在插入前验证对象 --filter arg 插入前过滤 --drop 在插入前删除所有文档 --oplogReplay 在恢复时应用oplog --oplogLimit arg include oplog entries before the provided Timestamp (seconds[:ordinal]) during the oplog replay; the ordinal value is optional --keepIndexVersion don't upgrade indexes to newest version --noOptionsRestore don't restore collection options --noIndexRestore don't restore indexes --w arg (= 0 ) minimum number of replicas per write |
恢复整个数据库:
1
|
# mongorestore -p 27017 dump/ |
恢复到特定的库和集合:
1
|
# mongorestore --db ttlsa_com --collection posts dump/old_ttlsa_com/old_posts.bson |
注意: mongodump和mongorestore版本最好相匹配。
通常情况下,在secondary进行备份,降低primary负载,只在secondary上锁定,以免影响业务(假设没有发送读请求到secondary)。 可以使用上面的任意方式进行备份,不过推荐使用文件快照方式和复制数据文件方式。
使用mongodump备份,上面提到了一个问题,那就是在mongodump备份过程中,发生写操作。在复制集架构环境下,要避免这种情况发生,mongodump需要加上--oplog参数,来跟踪备份时服务器上发生的所有操作,获取一个pointin-time快照,否则备份的状态将与集群中其他节点不匹配。在恢复时,还必须创建oplog,并指定--oplogReplay参数来应用这些操作,否则恢复的成员将不知道从何处开始同步,从而在某个时间点上与源服务器保持一致。
在备份复制集时,可以设置mongodump连接"setName/s1,s2,s3", 它会自动选择一个可用的secondary进行备份。
备份复制集:
1
|
# mongodump -h "ttlsa/10.1.1.155,10.1.1.156,10.1.1.157" --oplog -o /backup/mongodbbackup/ |
恢复复制集步骤:
1
|
# mongorestore --oplogReplay dump/ |
1
2
|
> use local > db.createCollection( "oplog.rs" , { "capped" : true , "size" : 10000000 }) |
1
|
# mongorestore -d local -c oplog.rs dump/oplog.bson |
注意:oplog.bson不位于dump/local/oplog.rs.bson, oplog.bson记录mongodump过程中发生的操作。
复制集的相关操作参见 http://www.ttlsa.com/html/1679.html
在分片集群下,不可能在一个时间点上得到一个完整集群状态的快照。当集群越来越大时,从备份恢复整个架构的几率越来越小的。 因此,对于分片集群的备份,只需独自备份config server和复制集。
在对分片集群进行备份与恢复操作前,要关闭balancer。
对于比较小的分片集群,可以直接从mongos来备份与恢复。
在大多数情况下,我们只需要恢复集群中的某个节点。 如果需要恢复整个集群,那你够倒霉的了,整个集群数据丢失可能性比较小的。备份时,直接连接分片集群的mongod而不是通过mongos。
对于比较小型的分片集群,可以直接通过mongodump连接到mongos进行备份,备份的文件将包含config服务器的元数据信息和实际数据。
对于大型的分片集群,备份步骤如下:
注意:连接到mongos而不是config server实例。
1
2
3
4
|
> sh.setBalancerState( false ) 或 > sh.stopBalancer() 或 > use config > db.settings.update( { _id: "balancer" }, { $ set : { stopped: true } } , true ); |
使用mongodump备份任意一台config server。
可以直接连接任意一台的config mongod实例,也可以通过mongos连接。
1
|
# mongodump --db config |
可并行执行。
注意:连接到mongos而不是config server实例。
1
2
3
4
|
> sh.setBalancerState( true ) 或 > sh.startBalancer() 或 > use config > db.settings.update( { _id: "balancer" }, { $ set : { stopped: false } } , true ); |
需要安装perl的MongoDB模块,安装方法参见:使用cpanm安装perl相关模块 http://www.ttlsa.com/html/2030.html 。代码这东西,仁者见仁智者见智,一分价钱一分货,所以对于优秀的程序员不要抠门。对语言的熟练度高,编程经验丰富的程序员,写出来的代码,两个字:漂亮!
下面的脚本只需更改变量$mongodb相关参数即可,如有更好的更便捷的方法请赐教。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
#!/bin/perl ################################# ### author: www.ttlsa.com ### ### QQ群: 39514058 ### ### E-mail: service@ttlsa.com ### ################################# use strict; use File::Path; use POSIX; use MongoDB; use Data::Dumper; my $mongodump= '/usr/local/mongodb/bin/mongodump' ; my $mongodb={ 'admin_1' =>{ #定义组别 'email' => '39514058@qq.com' , #定义备份出错时邮件通知地址,此处未包含该功能。 'server' =>[ #定义mongodb相关信息 { 'replset' => 1 , #是否复制集 'sharding' => 0 , #是否分片 'setname' => "TTLSA_COM" , #复制集名称 'mongodsvr' =>[ "10.1.11.155:27017" , "10.1.11.156:27017" , "10.1.11.157:27017" ], #mongod服务器IP和端口号 'configsvr' =>[], #config server服务器IP和端口号 'mongossvr' =>[], #mongos server服务器IP和端口号 'backupdir' => "/data/backup/mongodb_ttlsa_com" , #备份目录 'user' => "" , #用户名 'passwd' => "" , #密码 'interval' => 15 , #备份轮滚周期 }, { 'replset' => 1 , 'sharding' => 1 , 'setname' => "shard1" , 'mongodsvr' =>[ "10.1.22.21:27029" , "10.1.22.22:27029" , "10.1.22.23:27029" ], 'configsvr' =>[ "10.1.22.21:27028" , "10.1.22.22:27028" , "10.1.22.23:27028" ], 'mongossvr' =>[ "10.1.22.21:27027" , "10.1.22.22:27027" , "10.1.22.23:27027" ], 'backupdir' => "/data/backup/mongodb_shard1" , 'user' => "" , 'passwd' => "" , 'interval' => 15 , }, { 'replset' => 0 , 'sharding' => 0 , 'setname' => "shard1" , 'mongodsvr' =>[ "10.1.27.22:30000" , "10.1.27.22:30001" , "10.1.20.16" ], 'configsvr' =>[], 'mongossvr' =>[], 'backupdir' => "/data/backup/mongodb_standalon" , 'user' => "root" , 'passwd' => "www.ttlsa.com" , 'interval' => 15 , } ]}, # 'admin_2' =>{} }; while (my($group,$value)= each (%$mongodb)){ foreach my $node (@{$value->{ 'server' }}){ my $tmp_stdout = tmpnam(); my $tmp_stderr = tmpnam(); if ($node->{ 'replset' } && !$node->{ 'sharding' }){ my $dir = $node->{ 'backupdir' }; mkpath $dir unless -e $dir; my $ return =backup_rotate($dir,$node->{ 'interval' }); my $hosts = $node->{ 'setname' }. '/' .join( ',' , @{$node->{ 'mongodsvr' }}); if (($node->{ 'user' } ne '' ) && ($node->{ 'passwd' } ne '' )){ my $retval=system( "$mongodump -vvvvv -h $hosts -u $node->{'user'} -p $node->{'passwd'} --oplog -o $dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); } else { my $retval=system( "$mongodump -vvvvv -h $hosts --oplog -o $dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); } }elsif($node->{ 'sharding' }){ my $mongod_hosts=$node->{ 'setname' }. '/' .join( ',' , @{$node->{ 'mongodsvr' }}); my $mongos_hosts=$node->{ 'setname' }. '/' .join( ',' , @{$node->{ 'mongossvr' }}); my $mongod_dir = $node->{ 'backupdir' }. '/' . 'mongodsvr' ; my $config_dir = $node->{ 'backupdir' }. '/' . 'configsvr' ; mkpath $mongod_dir unless -e $mongod_dir; mkpath $config_dir unless -e $config_dir; my $ return =backup_rotate($mongod_dir,$node->{ 'interval' }); my $ return =backup_rotate($config_dir,$node->{ 'interval' }); if (($node->{ 'user' } ne '' ) && ($node->{ 'passwd' } ne '' )){ my $conn = MongoDB::Connection-> new ( "host" => "mongodb://$mongos_hosts" , "db_name" => "admin" , "username" => "$node->{'user'}" , "password" => "$node->{'passwd'}" ); my $db = $conn->get_database( 'config' ); my $coll=$db->get_collection( 'settings' ); my $ret=$coll->update({ _id => "balancer" }, { '$set' => { 'stopped' => 'true' } },{ 'false' },{ 'true' }); my $retval=system( "$mongodump -h $mongos_hosts -u $node->{'user'} -p $node->{'passwd'} --db config -o $config_dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); my $retval=system( "$mongodump -h $mongod_hosts -u $node->{'user'} -p $node->{'passwd'} --oplog -o $mongod_dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); my $ret=$coll->update({ _id => "balancer" }, { '$set' => { 'stopped' => 'false' } },{ 'false' },{ 'true' }); } else { my $db = $conn->get_database( 'config' ); my $coll=$db->get_collection( 'settings' ); my $ret=$coll->update({ _id => "balancer" }, { '$set' => { 'stopped' => 'true' } },{ 'false' },{ 'true' }); my $retval=system( "$mongodump -h $mongos_hosts --db config -o $config_dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); my $retval=system( "$mongodump -h $mongod_hosts --oplog -o $mongod_dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); my $ret=$coll->update({ _id => "balancer" }, { '$set' => { 'stopped' => 'false' } },{ 'false' },{ 'true' }); } } else { foreach my $ each (@{$node->{ 'mongodsvr' }}){ my $dir = $node->{ 'backupdir' }. '/' .$ each ; mkpath $dir unless -e $dir; my $ return =backup_rotate($dir,$node->{ 'interval' }); if (($node->{ 'user' } ne '' ) && ($node->{ 'passwd' } ne '' )){ my $retval=system( "$mongodump -h $each -u $node->{'user'} -p $node->{'passwd'} -o $dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); } else { my $retval=system( "$mongodump -h $each -o $dir/dump.1 >$tmp_stdout 2>$tmp_stderr" ); } } } } } sub backup_rotate { my $backup_dir=shift; my $retral=shift; my @aa=sort{$b<=>$a} 1 ..$retral; if (chdir "/$backup_dir" ){ for my $num (@aa){ my $old=$num+ 1 ; if (-e "dump.$num" ){ if (-e "dump.$old" ){ system( "rm -rf dump.$old && mv dump.$num dump.$old" ); } else { system( "mv dump.$num dump.$old" ); } } } } else { return "Failed to change dir $backup_dir : $!" ; } } |
上一篇《mongodb备份与恢复(上)》http://www.ttlsa.com/html/1938.html 说到的方法都需要拷贝所有数据,即使数据发生很少的改变。如果数据量很大的话,备份整个数据库将消耗更多的时间和磁盘空间。这时增量备份将会是个必然的选择,记录前一次的完整备份点,后续的备份只备份从该点发生改变的数据。
这种方法需要一台专门的备份服务器backup_server,当然backup_server需要一个完整的备份,然后通过mongooplog工具来拷贝并应用mongodb_server的oplog日志。
在mongodb_server上执行以下操作:
1
2
|
> op = db.oplog.rs.find().sort({$natural: - 1 }).limit( 1 ).next(); > start = op[ 'ts' ][ 't' ]/ 1000 |
在backup_server上执行:
1
|
# mongooplog --from A --seconds SECOND |
SECOND值位于start值与当前时间之间。
mongooplog工具介绍:
mongooplog从远程拉取oplog日志并应用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# ./mongooplog --help --help 显示帮助信息 -v [ --verbose ] 打印出更多信息,如时间等等 -vvvvv --version 打印版本信息 -h [ --host ] arg 指定连接的mongodb主机,复制集时设置为< set name>/s1,s2 --port arg 指定mongodb端口号,也可以这么指定--host hostname:port --ipv6 启用支持IPv6 -u [ --username ] arg 用户名 -p [ --password ] arg 密码 --dbpath arg 直接访问mongod的数据库文件,而不是连接到mongodb服务器。需要锁定数据目录,如果mongod当前在访问相同路径将不能使用。也就是说,mongod运行的情况下不能使用--dbpath,mongod未运行的情况下可以直接指定--dbpath --directoryperdb 每个db一个单独的目录,需要指定dbpath --journal 启用journaling -d [ --db ] arg 指定数据库 -c [ --collection ] arg 指定集合(some commands) -f [ --fields ] arg 逗号分隔的列名,如-f name,age --fieldFile arg file with fields names - 1 per line -s [ --seconds ] arg seconds to go back default : 86400 --from arg host to pull from --oplogns arg (=local.oplog.rs) ns to pull from |
提供支持按照指定时间点恢复的持续增量备份功能, 不过需要支付的。
提供的特性有:mongodb备份与恢复(下)
1. 用于数据传输的SSL加密
2. 高可用性
3. 指定时间点恢复
4. 支持分片集群
5. 较低的开销
工作原理:
是一个轻量级代理,它从所有正在备份的副本集合中收集oplog,将其压缩并加密,然后通过SSL发送到运行备份服务的数据中心。
此方法的好处有:
1. 数据是增量备份的,因此传输的数据相对较小
2. 备份服务中的数据与主系统中的数据在时间上非常接近
3. 对主系统的影响不会比向副本集合中添加另一个副本大(后者非常缓慢, 可先恢复到最新备份状态,然后在加入集群中同步)
4. oplog支持将副本集合恢复到任意时间点。
有两个备份选项:快照和自定义快照。
备份服务依据一套策略创建和维持备份快照。这些快照中的任何一个都可以用于恢复。同样的,用户也可以指定一个自己希望使用的精确时间点创建一份快照。在这种情况下,将使用该时间点之前最新的快照,并会根据用户指定的时间点应用oplog。
声明: 本文由( 爱说云网 )原创编译,转载请保留链接: mongodb备份与恢复方法大全