[ Mongodb ] 全量备份和增量备份
1. 前言
由于线上的mongodb 数据体量越来越大,如果没有完善的备份方案,发生故障势必造成业务很长时间的暂停。参考了网上方案,写出以下总结和备份方案:
备份方案分为两种:全备和增量备份,二者结合起来使用。
参考链接:https://www.cnblogs.com/xuliuzai/p/9917137.html 感谢作者的分享。
2. 构建mongodb 副本集测试环境
首先在测试环境进行测试,过程如下:
测试机:192.168.118.16 系统:CentOS 7
首先搭建mongodb 副本集(为了和线上环境保持一致)这里使用 mongodb 3.6 的版本,建议和生产环境相同的版本。
Mongdb 没啥安装的, 开箱即用。副本集参考链接:https://www.cnblogs.com/hukey/p/5769548.html
1 2 3 4 5 6 7 8 9 | rs0:PRIMARY> rs.isMaster(); { "hosts" : [ "192.168.118.16:27017" , "192.168.118.16:27018" , "192.168.118.16:27019" ], … … |
副本集创建成功。
接下来,向集群里写入数据:
1 2 3 4 5 6 7 8 9 10 11 | rs0:PRIMARY> for (var i=1;i<=10000;i++) db. users .insert({ id :i, name: "hukey" ,city: "xi'an" }); WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> show dbs; admin 0.000GB config 0.000GB local 0.000GB test 0.000GB rs0:PRIMARY> use test switched to db test rs0:PRIMARY> db. users .count(); 10000 |
写入了 1W 条数据。
准备工作完成。
3. mongodb 全量备份及恢复
全备脚本 [ mongodb_back_all.sh ] 如下:
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 | #!/bin/bash # Author:hukey host= '192.168.118.16' port= '27017' sourcepath= '/mongodb/bin' targetpath= '/mongodb/backup' nowtime=$( date "+%Y%m%d" ) start(){ $sourcepath /mongodump --host $host --port $port --oplog -- gzip --out ${targetpath}/${nowtime} } execute(){ echo "=========================$(date) backup all mongodb back start ${nowtime}=========" start if [ $? - eq 0 ]; then echo "The MongoDB BackUp Successfully!" else echo "The MongoDB BackUp Failure" fi } if [ ! -d "${targetpath}/${nowtime}" ]; then mkdir -p "${targetpath}/${nowtime}" fi execute backtime=$( date -d '-7 days' "+%Y%m%d" ) if [ -d "${targetpath}/${backtime}/" ]; then rm -rf "${targetpath}/${backtime}/" echo "=======${targetpath}/${backtime}/===删除完毕==" fi echo "========================= $(date) backup all mongodb back end ${nowtime}=========" |
全库还原脚本 [ mongodb_restore_all.sh ] 如下:
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 | #!/bin/bash # Author:hukey echo -e "\033[31;1m*****[ Mongodb ] 全库恢复脚本*****\033[0m" host=192.168.118.16 mongo_bin= /mongodb/bin/ backpath= '/mongodb/backup' echo -e "\033[32;1m[ 选择要恢复全库的日期 ] \033[0m" for backfile in ` ls $backpath`; do echo $backfile done read -p ">>>" date_bak if [[ $date_bak == "" ]] || [[ $date_bak == '.' ]] || [[ $date_bak == '..' ]]; then echo -e "\033[31;1m输入不能为特殊字符.\033[0m" exit 1 fi if [ -d $backpath/$date_bak ]; then read -p "请确认是否恢复全库备份[y/n]:" choice if [ "$choice" == "y" ]; then echo -e "\033[32;1m正在恢复全库备份,请稍后...\033[0m" $mongo_bin /mongorestore --host $host --port 27017 --oplogReplay -- gzip $backpath/$date_bak/ if [ $? - eq 0 ]; then echo -e "\033[32;1m--------全库恢复成功.--------\033[0m" else echo -e "\033[31;1m恢复失败,请手动检查!\033[0m" exit 3 fi else exit 2 fi else echo "\033[31;1m输入信息错误.\033[0m" exit 1 fi |
执行全量备份脚本:
1 2 3 4 5 6 7 8 9 10 | [root@192.168.118.16 /mongodb/script ] #sh mongodb_back_all.sh =========================Thu Sep 12 11:57:32 CST 2019 backup all mongodb back start 20190912========= 2019-09-12T11:57:32.863+0800 writing admin.system.version to 2019-09-12T11:57:32.866+0800 done dumping admin.system.version (1 document) 2019-09-12T11:57:32.867+0800 writing test . users to 2019-09-12T11:57:32.955+0800 done dumping test . users (10000 documents) 2019-09-12T11:57:32.956+0800 writing captured oplog to 2019-09-12T11:57:32.975+0800 dumped 1 oplog entry The MongoDB BackUp Successfully! ========================= Thu Sep 12 11:57:32 CST 2019 backup all mongodb back end 20190912========= |
查看备份数据:
1 2 | [root@192.168.118.16 /mongodb/script ] #ls /mongodb/backup/20190912/ admin oplog.bson test |
在测试全库还原之前,首先需要清库数据(注意:本次操作是在测试环境)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | rs0:PRIMARY> use test ; switched to db test rs0:PRIMARY> db. users .count(); 10000 rs0:PRIMARY> db.dropDatabase(); { "dropped" : "test" , "ok" : 1, "operationTime" : Timestamp(1568264437, 2), "$clusterTime" : { "clusterTime" : Timestamp(1568264437, 2), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } |
执行全库恢复脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [root@192.168.118.16 /mongodb/script ] #sh mongodb_restore_all.sh *****[ Mongodb ] 全库恢复脚本***** [ 选择要恢复全库的日期 ] 20190912 >>>20190912 请确认是否恢复全库备份[y /n ]:y 正在恢复全库备份,请稍后... 2019-09-12T13:43:58.956+0800 preparing collections to restore from 2019-09-12T13:43:58.959+0800 reading metadata for test . users from /mongodb/backup/20190912/test/users .metadata.json.gz 2019-09-12T13:43:59.019+0800 restoring test . users from /mongodb/backup/20190912/test/users .bson.gz 2019-09-12T13:44:01.950+0800 [ #################.......] test.users 28.0KB/37.9KB (73.9%) 2019-09-12T13:44:04.083+0800 [ ########################] test.users 37.9KB/37.9KB (100.0%) 2019-09-12T13:44:04.083+0800 no indexes to restore 2019-09-12T13:44:04.084+0800 finished restoring test . users (10000 documents) 2019-09-12T13:44:04.084+0800 replaying oplog 2019-09-12T13:44:04.084+0800 done --------全库恢复成功.-------- |
全库的备份和还原已经实现,可以通过 crontab 来制定计划任务触发。
4. mongodb 增量备份及恢复
增量备份的思路是通过 oplog 来实现的,大家可以通过文档搜索了解。
直接上脚本:
增量备份 [ mongodb_backup_incremental.sh ] 脚本
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 | #!/bin/bash # Author:hukey command_linebin= '/mongodb/bin/mongo' port=27017 if [ ! -d "/mongodb/backup/mongodbOplog_bak/mongo-$port" ]; then mkdir -p /mongodb/backup/mongodbOplog_bak/mongo- $port fi if [ ! -d "/mongodb/backup/mongodbOplog_bak/log-$port" ]; then mkdir -p /mongodb/backup/mongodbOplog_bak/log- $port fi bkdatapath= /mongodb/backup/mongodbOplog_bak/mongo- $port bklogpath= /mongodb/backup/mongodbOplog_bak/log- $port logfilename=$( date + "%Y%m%d" ) echo "===MongoDB 端口为" $port "的差异备份开始,开始时间为" $( date -d today + "%Y%m%d%H%M%S" ) paramBakEndDate=$( date +%s) echo "===本次备份时间参数中的结束时间为:" $paramBakEndDate diffTime=$( expr 60 \* 60) echo "===备份设置的间隔时间为:" $diffTime paramBakStartDate=$( expr $paramBakEndDate - $diffTime) echo "===本次备份时间参数中的开始时间为:" $paramBakStartDate diffTime=$( expr 61 \* 60) paramAfterBakRequestStartDate=$( expr $paramBakEndDate - $diffTime) echo "===为保证备份的连续性,本次备份后,oplog中的开始时间需小于:" $paramAfterBakRequestStartDate bkfilename=$( date -d today + "%Y%m%d%H%M%S" ) command_line= "${command_linebin} 192.168.118.16:27017" opmes=$( /bin/echo "db.printReplicationInfo()" | $command_line --quiet) echo $opmes > /tmp/opdoctime $port.tmplog opbktmplogfile= /tmp/opdoctime $port.tmplog opstartmes=$( grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' ) oplogRecordFirst=$( date -d "$opstartmes" +%s) echo "===oplog集合记录的开始时间为[格式化]:" $oplogRecordFirst if [ $oplogRecordFirst - le $paramBakStartDate ]; then echo "Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。" else echo "Fatal Error --检查设置的备份时间不合理合理。备份参数的开始时间不在oplog记录的时间范围内。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。" fi /mongodb/bin/mongodump -h 192.168.118.16 --port $port -d local -c oplog.rs --query '{ts:{$gte:Timestamp(' $paramBakStartDate ',1),$lte:Timestamp(' $paramBakEndDate ',9999)}}' -o $bkdatapath /mongodboplog $bkfilename opmes=$( /bin/echo "db.printReplicationInfo()" | $command_line --quiet) echo $opmes > /tmp/opdoctime $port.tmplog opbktmplogfile= /tmp/opdoctime $port.tmplog opstartmes=$( grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' ) oplogRecordFirst=$( date -d "$opstartmes" +%s) echo "===执行备份后,oplog集合记录的开始时间为[时间格式化]:" $oplogRecordFirst if [ $oplogRecordFirst - le $paramAfterBakRequestStartDate ]; then echo "Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部op操作,满足文件的持续完整性,逐个还原无丢失数据风险。" else echo "Fatal Error --备份后,检查oplog集合的涵盖的时间范围过小(小于61min)。设置的备份时间不合理合理,备份后的文件不能完全涵盖最近60分钟的数据。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。" fi if [ -d "$bkdatapath/mongodboplog$bkfilename" ] then echo "Message --检查此次备份文件已经产生.文件信息为:" $bkdatapath /mongodboplog $bkfilename >> $bklogpath/$logfilename.log else echo "Fatal Error --备份过程已执行,但是未检测到备份产生的文件,请检查!" >> $bklogpath/$logfilename.log fi keepbaktime=$( date -d '-3 days' "+%Y%m%d%H" )* if [ -d $bkdatapath /mongodboplog $keepbaktime ]; then rm -rf $bkdatapath /mongodboplog $keepbaktime echo "Message -- $bkdatapath/mongodboplog$keepbaktime 删除完毕" >> $bklogpath/$logfilename.log fi echo "===MongoDB 端口为" $port "的差异备份结束,结束时间为:" $( date -d today + "%Y%m%d%H%M%S" ) |
这个脚本比较长,需要注意一个变量: diffTime
第一次定义这个变量的时候,是为了定义备份的时长,从此刻到之前 65 * 60ms 之前的时间,也就是备份从现在到之前 1小时5分这段时间的增量;
第二次定义这个变量的时候,是为了避免数据增长过快,覆盖了还未备份的数据的,比较的依据是 mongodb db.printReplicationInfo(); 的 oplog first event time 时间。
这两个定义的时间可灵活调整。
增量备份还原 [ mongodb_backup_incremental.sh ] 脚本
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 | #!/bin/bash # Author:hukey host=192.168.118.16 port=27017 mongo_bin= /mongodb/bin/ backpath= '/mongodb/backup/mongodbOplog_bak/mongo-27017' echo -e "\033[31;1m*****[ Mongodb ] 增量恢复脚本*****\033[0m" echo -e "\033[32;1m[ 选择要恢复增量的日期(格式:年月日时分秒) ] \033[0m" for time_file in ` ls $backpath`; do echo $time_file done read -p ">>>" date_bak if [[ $date_bak == "" ]] || [[ $date_bak == '.' ]] || [[ $date_bak == '..' ]]; then echo -e "\033[31;1m输入不能为特殊字符.\033[0m" exit 1 fi if [ -d $backpath/$date_bak ]; then read -p "请确认是否恢复[$date_bak]增量备份[y/n]:" choice if [ "$choice" == "y" ]; then mkdir -p /tmp/mongodb/ && cp -a $backpath/$date_bak /local/oplog .rs.bson /tmp/mongodb/oplog .bson $mongo_bin /mongorestore --host $host --port $port --oplogReplay /tmp/mongodb/ && rm -rf /tmp/mongodb/ if [ $? - eq 0 ]; then echo -e "\033[32;1m--------[$date_bak]增量恢复成功.--------\033[0m" else echo -e "\033[31;1m恢复失败,请手动检查!\033[0m" exit 3 fi else exit 2 fi else echo -e "\033[31;1m输入信息错误.\033[0m" exit 1 fi |
测试下增量备份脚本和还原增量脚本
(1)首先写入一批数据到 mongodb
1 2 3 4 5 6 | rs0:PRIMARY> for (var i=1;i<=100;i++) db. users .insert({ id :i,name: "hukey" }); WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> use test ; switched to db test rs0:PRIMARY> db. users .count(); 100 |
写入了 100 条数据成功。
(2)执行增量备份脚本
1 2 3 4 5 6 7 8 9 10 11 12 | [root@192.168.118.16 /mongodb/script ] #sh mongodb_backup_incremental.sh ===MongoDB 端口为 27017 的差异备份开始,开始时间为 20190912141545 ===本次备份时间参数中的结束时间为: 1568268945 ===备份设置的间隔时间为: 3900 ===本次备份时间参数中的开始时间为: 1568265045 ===为保证备份的连续性,本次备份后,oplog中的开始时间需小于: 1568265285 ===oplog集合记录的开始时间为[格式化]: 1568260252 Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。 2019-09-12T14:15:48.648+0800 Failed: error connecting to db server: no reachable servers ===执行备份后,oplog集合记录的开始时间为[时间格式化]: 1568260252 Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部 op 操作,满足文件的持续完整性,逐个还原无丢失数据风险。 ===MongoDB 端口为 27017 的差异备份结束,结束时间为: 20190912141548 |
(3)再次新增 100 条数据到 mongodb
1 2 3 4 5 6 | rs0:PRIMARY> use test switched to db test rs0:PRIMARY> for (var i=1;i<=100;i++) db. users .insert({ id :i,name: "xiaofei" }); WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> db. users .count(); 200 |
(4)再次执行增量备份
1 2 3 4 5 6 7 8 9 10 11 12 | [root@192.168.118.16 /mongodb/script ] #sh mongodb_backup_incremental.sh ===MongoDB 端口为 27017 的差异备份开始,开始时间为 20190912141728 ===本次备份时间参数中的结束时间为: 1568269048 ===备份设置的间隔时间为: 3900 ===本次备份时间参数中的开始时间为: 1568265148 ===为保证备份的连续性,本次备份后,oplog中的开始时间需小于: 1568265388 ===oplog集合记录的开始时间为[格式化]: 1568260252 Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。 2019-09-12T14:17:31.696+0800 Failed: error connecting to db server: no reachable servers ===执行备份后,oplog集合记录的开始时间为[时间格式化]: 1568260252 Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部 op 操作,满足文件的持续完整性,逐个还原无丢失数据风险。 ===MongoDB 端口为 27017 的差异备份结束,结束时间为: 20190912141731 |
到此,已知:
第一次增量备份前,数据是 100 条
第二次增量备份前,数据是 200 条
(5)删除 mongodb 数据,恢复第一次增量备份数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | rs0:PRIMARY> db. users .count(); 200 rs0:PRIMARY> db.dropDatabase(); { "dropped" : "test" , "ok" : 1, "operationTime" : Timestamp(1568270110, 2), "$clusterTime" : { "clusterTime" : Timestamp(1568270110, 2), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } rs0:PRIMARY> rs0:PRIMARY> show dbs; admin 0.000GB config 0.000GB local 0.001GB |
执行恢复增量备份的脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [root@192.168.118.16 /mongodb/script ] #sh mongodb_restore_incremental.sh *****[ Mongodb ] 增量恢复脚本***** [ 选择要恢复增量的日期(格式:年月日时分秒) ] mongodboplog20190912144121 mongodboplog20190912144403 >>>mongodboplog20190912144121 请确认是否恢复[mongodboplog20190912144121]增量备份[y /n ]:y 2019-09-12T14:44:34.534+0800 preparing collections to restore from 2019-09-12T14:44:34.534+0800 replaying oplog 2019-09-12T14:44:37.531+0800 oplog 1.12MB 2019-09-12T14:44:40.021+0800 oplog 1.79MB 2019-09-12T14:44:40.021+0800 done --------[mongodboplog20190912144121]增量恢复成功.-------- |
此时,mongodb 应该是 100 条数据才对,验证下:
1 2 3 4 | rs0:PRIMARY> use test ; switched to db test rs0:PRIMARY> db. users .count(); 100 |
数据正确,执行第二次增量备份的还原:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [root@192.168.118.16 /mongodb/script ] #sh mongodb_restore_incremental.sh *****[ Mongodb ] 增量恢复脚本***** [ 选择要恢复增量的日期(格式:年月日时分秒) ] mongodboplog20190912144121 mongodboplog20190912144403 >>>mongodboplog20190912144403 请确认是否恢复[mongodboplog20190912144403]增量备份[y /n ]:y 2019-09-12T14:45:41.886+0800 preparing collections to restore from 2019-09-12T14:45:41.887+0800 replaying oplog 2019-09-12T14:45:44.882+0800 oplog 1.04MB 2019-09-12T14:45:47.882+0800 oplog 1.75MB 2019-09-12T14:45:50.341+0800 oplog 1.81MB 2019-09-12T14:45:50.341+0800 done --------[mongodboplog20190912144403]增量恢复成功.-------- |
此时,mongodb 应该是 200 条数据才对,验证下:
1 2 3 4 | rs0:PRIMARY> use test ; switched to db test rs0:PRIMARY> db. users .count(); 200 |
正确,数据恢复没有问题。增量备份完成。
5. 后记
对于线上生产环境,目前的备份解决方案是:
全量备份 1 周执行一次,增量备份每天执行一次。
后期准备在建立一个备用的副本集,每天都会将备份数据还原到 新建的副本集中,进行数据备份的校验,准备还是通过脚本来实现。后期在编写。
本文作者:hukey
本文链接:https://www.cnblogs.com/hukey/p/11512062.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· .NET 8.0 + Linux 香橙派,实现高效的 IoT 数据采集与控制解决方案
· .NET中 泛型 + 依赖注入 的实现与应用