1. 程式人生 > >瞧一瞧!看一看!這兒實現了MongoDB的增量備份與還原(含部署程式碼)

瞧一瞧!看一看!這兒實現了MongoDB的增量備份與還原(含部署程式碼)

一 需求描述

我們知道資料是公司的重要資產,業務的系統化、資訊化就是數字化。資料高效的儲存與查詢是系統完善和優化的方向,而資料庫的穩定性、可靠性是實現的基礎。高可用和RPO(RecoveryPointObjective,復原點目標,指能容忍的最大資料丟失量)是衡量一個數據庫優劣的重要指標。作為一個DBA,搭建資料庫可靠性體系時,一定會要考慮對資料庫進行容災備份。例如,SQL Server型別的資料庫,我們一定會部署作業,定期進行完整備份、差異備份和日誌備份;MySQL 資料庫同樣如此,也是定期進行完整備份、binlog備份等。

可能很多公司的DBA認為自己的資料庫已採用了新的高可用方案,是多結點冗餘了,不再需要冗餘備份了,例如SQL Server 的AlwaysOn,MySQL的MHA。可是,我們還是要強調兩點。

1.墨菲定律:如果有兩種或兩種以上的方式去做某件事情,而其中一種選擇方式將導致災難,則必定有人會做出這種選擇。

過往無數的慘痛教訓說明,將損失放大的原因就是備份資料也損壞了(大家不重視,實際上很可能就沒做)。

2.容災備份不僅僅可以解決物理故障,還可以將一些其它誤操作回滾,將資料的損害降至最低。

資料容災備份是為資料的災難恢復加固了最後一道保障牆。

國務院資訊化工作辦公室領導編制的《重要資訊系統災難恢復指南》也對災難恢復能力等級做了詳細劃分。

       災難恢復能力等級

RTO

                      RPO

1

2天以上

1天至7天

2

24小時以後

1天至7天

3

12小時以上

數小時至1天

4

數小時至2天

數小時至1天

5

數分鐘至2天

0至30分鐘

6

數分鐘

0

隨著MongoDB使用的越來越普及,儲存的資料越來越重要,對其進行定期備份很有必要。現在業內普遍流行的做法是每天定時進行一次全庫備份,出故障時,進行全庫還原。但是一天一次的備份,很難保證恢復後的資料時效性,RPO較差,會有幾個小時的資料丟失。例如,每天5點進行完整備份,如果故障點是晚上20:00,那麼就會丟失15個小時的資料。對比上面的“災難恢復能力等級“”列表,會發現,我們的災難能力等級比較低。

如果每小時做一次全部備份,那麼對儲存空間的要求較高,還有就是對效能也會有影響。所以,探究Mongodb 增量備還原很有必要

二 原理說明

關係型資料庫,例如MySQL ,SQL Server 都有事務日誌(或bin log),會將資料庫的DML 、DDL、DCL等操作記錄在事務檔案中,可以通過日誌備份來搭建增量容災還原體系。MongoDB沒有此類機制和資料檔案,難以實現。但是MongoDB副本集有通過oplog(位於local資料庫oplog.rs集合中) 實現節點間的同步,此集合記錄了整個mongod例項在一段時間內的所有變更(插入/更新/刪除)操作。基於此,是否可以考慮通過oplog.rs集合的備份還原來實現例項的增量備份與增量還原。

檢視mongodb備份命令Mongodump,其中有一個相關引數oplog。

Mongodump --oplog引數

引數

引數說明

--oplog

Use oplog for taking a point-in-time snapshot

該引數的主要作用是在匯出庫集合資料的同時生成一個oplog.bson檔案,裡面存放了開始進行dump到dump結束之間所有的op log 操作。

注意:--oplog選項只對全庫匯出有效。

相應的 Mongorestore 中與 Oplog 相關的引數

引數

引數說明

oplogReplay

replay oplog for point-in-time restore

oplogLimit

only include oplog entries before the provided Timestamp

oplogFile

oplog file to use for replay of oplog

仔細觀察oplogReplay引數下的還原過程,我們發現,是先還原資料庫檔案,再重放還原oplog.bson種的資料。這就啟發了我們,如果還原路徑下,只有oplog.bson檔案,沒有資料庫備份檔案,是不是隻進行重放還原操作。如此,如果oplog.bson中記錄都是上次備份後的變化操作(op log),還原oplog.bson就可以實現了增量還原。考慮到副本集的變化操作(op log)儲存在oplog.rs集合中,只要連續從oplog.rs匯出操作的相關資料進行備份,就可以實現增量備份。

即理論上,從oplog.rs匯出的資料完全可以替代mongodump過程中產生的oplog.bson,進行增量還原。

實際生產中經過多次驗證,也是完全可以。

-----具體原理及驗證內容還可參考本人部落格 --https://www.cnblogs.com/xuliuzai/p/9832333.html

三 程式碼實現【重點推薦

在容災體系建設中,既有全庫的完整備份也有增量備份。下面是我們的實現程式碼,因為MongoDB多是在Linux系統下部署,所以這些備份程式碼都是通過shell語言實現。這些程式碼大家只要稍微改動,調整部分引數,即可部署應用。

1. 實現完整(全庫)備份的程式碼

#!/bin/bash
sourcepath='/data/mongodb/mongobin344/bin'
targetpath='/data/mongodb_back/bkXXX_2XXXX'
nowtime=$(date "+%Y%m%d")
start()
{
${sourcepath}/mongodump --host 172.XXX.XXX.XXX --port 2XXXX -u 使用者名稱-p "密碼" --oplog --gzip --authenticationDatabase "admin" --out ${targetpath}/${nowtime}
}
execute()
{

echo "================================ $(date) bakXXX 2XXXX mongodb back start  ${nowtime}========="

start
if [ $? -eq 0 ]
then
echo "The MongoDB BackUp Successfully!"
else "The MongoDB BackUp Failure"
fi
}
if [ ! -d "${targetpath}/${nowtime}/" ]
then
  mkdir ${targetpath}/${nowtime}
fi
execute

baktime=$(date -d '-3 days' "+%Y%m%d")
if [ -d "${targetpath}/${baktime}/" ]
then
  rm -rf "${targetpath}/${baktime}/"
  echo "=======${targetpath}/${baktime}/===刪除完畢=="
fi

echo "================================ $(date) bakXXX 2XXXX mongodb back end ${nowtime}========="

程式碼說明:

1.完整備份的指令碼,通過crontab觸發執行,每天執行一次。

2.備份完整後,會將三天前的備份檔案自動刪除。

3.sourcepath 定義了MongoDB 執行程式所在路徑;targetpath定義了歸檔檔案存放的資料夾(請提前建立)。

2. 實現增量備份的程式碼

#
# This file is used by cron to Backup the data of oplog collection,the collection is part of local DB.
# The oplog (operations log) is a special capped collection that keeps a rolling record of all operations 
# that modify the data stored in your databases.All replica set members contain a copy of the oplog, 
# in the local.oplog.rs collection, which allows them to maintain the current state of the database.
# Each operation in the oplog is idempotent. That is, oplog operations produce the same results 
# whether applied once or multiple times to the target dataset.
#
# We backup the collections by periodicity to restore the DB  in case of  DB disaster 
# The version is defined V.001
# Version   ModifyTime                ModifyBy              Desc
# Ver001    2018-11-06 17:00         xuchangpei             Create the Scripts File
#
#
#!/bin/bash

#### 請在此處輸入關鍵引數,例如程式路徑,賬號,密碼,例項埠###
command_linebin="/data/mongodb/mongobin344/bin/mongo"
username="使用者名稱"
password="使用者命名"
port="mongo都被的埠號"
####
####comments0 start 第一次執行此指令碼時,自動檢查建立備份路徑 ####
if [ ! -d "/data/mongodb_back/mongodboplog_back/mongo$port" ]
then
  mkdir -p /data/mongodb_back/mongodboplog_back/mongo$port
fi

if [ ! -d "/data/mongodb_back/mongodboplog_back/log/$port" ]
then
  mkdir -p /data/mongodb_back/mongodboplog_back/log/$port
fi

bkdatapath=/data/mongodb_back/mongodboplog_back/mongo$port
bklogpath=/data/mongodb_back/mongodboplog_back/log/$port

####comments end ##

logfilename=$(date -d today +"%Y%m%d")

echo "===================================Message --=MongoDB 埠為" $port "的差異備份開始,開始時間為" $(date -d today +"%Y%m%d%H%M%S") >> $bklogpath/$logfilename.log

ParamBakEndDate=$(date +%s)
echo "Message --本次備份時間引數中的結束時間為:" $ParamBakEndDate >> $bklogpath/$logfilename.log

DiffTime=$(expr 65 \* 60)

echo "Message --備份設定的間隔時間為:" $DiffTime >> $bklogpath/$logfilename.log


ParamBakStartDate=$(expr $ParamBakEndDate - $DiffTime)
echo "Message --本次備份時間引數中的開始時間為:" $ParamBakStartDate >> $bklogpath/$logfilename.log

bkfilename=$(date -d today +"%Y%m%d%H%M%S")

#### comments1 start 獲取資料庫中oplog記錄的開始範圍,防止匯出的資料不完整 ####

command_line="${command_linebin} localhost:$port/admin -u$username -p$password"

opmes=$(/bin/echo "db.printReplicationInfo()" | $command_line --quiet)

echo $opmes > opdoctime$port.tmplog

opbktmplogfile=opdoctime$port.tmplog

#opstartmes=$(grep "oplog first event time" $opmes)

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}'  )

echo "Message --oplog集合記錄的開始時間為:"$opstartmes >> $bklogpath/$logfilename.log

oplogRecordFirst=$(date -d "$opstartmes"  +%s)

echo "Message --oplog集合記錄的開始時間為:" $oplogRecordFirst >> $bklogpath/$logfilename.log

##begin 比較備份引數的開始時間是否在oplog記錄的時間範圍內
if [ $oplogRecordFirst -le $ParamBakStartDate ]
then
echo "Message --檢查設定備份時間合理。備份引數的開始時間在oplog記錄的時間範圍內。" >> $bklogpath/$logfilename.log
else echo "Fatal Error --檢查設定的備份時間不合理合理。備份引數的開始時間不在oplog記錄的時間範圍內。請調整oplog size或調整備份頻率。本次備份可以持續進行,但還原時資料完整性丟失。" >> $bklogpath/$logfilename.log
fi

##end##

#### comments1 end  ####

dumpmsg=$(/data/mongodb/mongobin344/bin/mongodump -h localhost --port $port --authenticationDatabase admin -u$username -p$password -d local -c oplog.rs  --query '{ts:{$gte:Timestamp('$ParamBakStartDate',1),$lte:Timestamp('$ParamBakEndDate',9999)}}' -o $bkdatapath/mongodboplog$bkfilename)

echo "本次匯出的具體資訊如下:" $dumpmsg
echo $dumpmsg >> $bklogpath/$logfilename.log

#### comments2 start  再次檢查,防止匯出oplog資料過程耗時過長,比如,我們一小時匯出一份,每一次迴圈涵蓋65分鐘,如果匯出執行過程耗時5分鐘以上就可能導致匯出的資料不完整。####
## 下面的70 是有上面的65+5而得,+5 是允許匯出耗時5分鐘。這個邏輯有點繞,大家可以測測,這段邏輯看幾分鐘可以理解通透了。
DiffTime=$(expr 70 \* 60)
AllowMaxDate=$(expr $(date +%s) - $DiffTime)
if [ $AllowMaxDate -le $ParamBakStartDate ]
then
echo "Message --oplog記錄匯出時間在規定的DiffTime範圍內。資料有效" >> $bklogpath/$logfilename.log
else echo "Fatal Error --oplog記錄匯出時間 超出了 規定的DiffTime範圍。資料完整性等不到保證。請增大DiffTime引數或調整備份頻率。" >> $bklogpath/$logfilename.log
fi

#### comments2 end ####

#### comments3 檢查備份檔案是否已經刪除start ####
if [ -d "$bkdatapath/mongodboplog$bkfilename" ]
then
  echo "Message --檢查此次備份檔案已經產生.檔案資訊為:" $bkdatapath/mongodboplog$bkfilename >> $bklogpath/$logfilename.log
  else echo "Fatal Error --備份過程已執行,但是未檢測到備份產生的檔案,請檢查!" >> $bklogpath/$logfilename.log
fi
##### comments3 end ####

#### comments4 start 刪除歷史備份檔案,保留3天,如需調整,請在持續設定
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
### comments4 end 


echo "============================Message --MongoDB 埠為" $port "的差異備份結束,結束時間為:" $(date -d today +"%Y%m%d%H%M%S") >> $bklogpath/$logfilename.log

程式碼說明:

1.增量備份的指令碼,也是通過crontab觸發執行,以上引數未修改前,建議每小時執行一次。

2.備份完整後,會自動檢查檔案是否產生,並且會將三天前的備份檔案刪除。

3.指令碼會自動檢查備份路徑,不存在將自動產生。

4.增量匯出中開始時間和結束時間是最重要的引數,並且要對引數的合法性、有效性檢查。例如,檢查Oplog的記錄是否完全涵蓋輸入的時間,防止出現希望匯出08:00--09:00的資料,但是oplog集合中只有08:30--09:00的資料;防止匯出過程耗時過長(例如超過定義的5分鐘),導致資料不完整。程式碼中都會對這些異常進行判斷和捕獲。

四 功能測試驗證

1. 測試環境

Item ServerIP Port User DB
源庫 172.XXX.XXX.124(Primary) 2XXX30 testoplog
備份還原庫 172.XXX.XXX.124(Primary) 2XXX20

2. 完整備份與還原

step 1 在備份前,先向資料庫testoplog插入部分資料

step 2 完整備份所有的資料庫,執行的程式碼為上面的完整備份程式碼(儲存到執行檔案bkoplogtest_2XXX30),打印出執行過程如下截圖

step 3 還原完整備份,執行的程式碼和列印執行過程如下:

執行的命令:

/data/mongodb/mongobin344/bin/mongorestore -h 172.XXX.XXX.124 --port 2XXX20  --oplogReplay --authenticationDatabase 認證資料庫-u 使用者名稱-p '密碼' --gzip /data/mongodb_back/testoplogbackfile/20181107

打印出的執行過程:

step 4 檢查還原庫的情況,檢查庫(testoplog)、表(testfullbefore01、testfullbefore02、testfullbefore03)是否還原。

結論:完整還原後與原庫完整備份時資料一致,符合測試預期。

 3. 增量備份與還原

增量備份與還原的測試案例描述

測試案例 第一次增量備份 第一次增量還原 第二次增量備份 第二次增量還原
源庫

備份前,新建集合testdiffbk01

並插入10000筆資料

 無操作

備份前,新建集合testdiffbk02

並插入10000筆資料

 無操作
還原庫 無操作

還原後,檢查testdiffbk01是否存在

以及資料量

 無操作

還原後,檢查testdiffbk02是否存在

以及資料量

step 1 第一次增量備份前,向源庫中插入測試資料

step 2 第一次執行增量備份(執行增量備份的指令碼,程式碼放置在執行檔案mongodb_oplogbacktestoplog2XXXX.sh中)

 

step 3 向源庫中第二次插入測試資料

step 4 第二次執行增量備份

兩次增量備份產生的檔案在 資料夾 /data/mongodb_back/mongodboplog_back/mongo27230 中,如下圖所示:

step 5 將完整備份所在路徑下的檔案清空,將第一次備份的產生的oplog.rs.bson 檔案,copy至此路徑下,並重命名為oplog.bson。【即還原第一份增量備份

清空指令:

Copy+ 重新命名指令

還原指令:

/data/mongodb/mongobin344/bin/mongorestore -h 172.XXX.XXX.124 --port 2XXX20 --oplogReplay --authenticationDatabase 驗證資料庫-u 使用者名稱-p '密碼' /data/mongodb_back/testoplogbackfile/20181107

 

step 7 驗證第一次增量還原的資料,驗證測試所用的集合testdiffbk01及資料,與原庫第一次增量備份時一致,即已正常還原增量。

step 8 將完整備份所在路徑下的檔案清空,將第二次增量備份的產生的oplog.rs.bson 檔案,copy至此路徑下,並重命名為oplog.bson。【即還原第二份增量備份

刪除指令

copy + 重新命名指令

還原增量備份的指令【與第一次執行的還原命令完全一樣】

/data/mongodb/mongobin344/bin/mongorestore -h 172.XXX.XXX.124 --port 2XXX20  --oplogReplay --authenticationDatabase 驗證資料庫-u 使用者名稱-p '密碼' /data/mongodb_back/testoplogbackfile/20181107

 

step 9  驗證第二次增量還原的資料,驗證測試所用的集合testdiffbk02及資料。結論:與原庫第二次增量備份時一致,即已正常還原增量。

 此次測試有完整備份與完整還原,還有兩次增量備份月增量,詳細演示增量還原方案的可行性和相關程式碼的可執行性,部署後滿足生產所需。

五 注意事項

一定要在還原完整備份的路徑下,還原已備份oplog的增量檔案。即先將已還原的完整備份檔案刪除,再將增量備份產生oplog.rs.bson檔案copy至路徑下,並且重新命名為oplog.bson。

如果是在其他路徑下,則報錯,主要的錯誤資訊為:

2018-11-06T10:24:51.848+0800 checking for collection data in /data/mongodb_back/bkrcs_test/oplog.bson2018-11-06T10:24:51.848+0800 Failed: no oplog file to replay; make sure you run mongodump with --oplog

驗證測試,完整備份(全庫備份)的檔案在 路徑  /XXX/XXXX_back/bkrcs_2XXXX/20181105 下 。

如果我們將oplog的增量檔案(oplog.rs集合匯出的資料)/local/oplog.rs.bson 複製至 /XXX/XXXX_back/bkrcs_test/路徑下,並重命名為oplog.bson

執行restore命令報錯:

如果我們將/local/oplog.rs.bson複製至還原完整備份所在的路徑下( /XXX/XXXX_back/bkrcs_27XXX/20181105),執行restore,測試不再報錯。

 

檢查新增資料也已同步過去。

所以,還原時增量備份(oplog)一定要放置完整備份所在的資料夾下(copy前,先將完整備份完結刪除)進行還原。