一 需求描述 我們知道數據是公司的重要資產,業務的系統化、信息化就是數字化。數據高效的存儲與查詢是系統完善和優化的方向,而資料庫的穩定性、可靠性是實現的基礎。高可用和RPO(RecoveryPointObjective,複原點目標,指能容忍的最大數據丟失量)是衡量一個資料庫優劣的重要指標。作為一個D ...
一 需求描述
我們知道數據是公司的重要資產,業務的系統化、信息化就是數字化。數據高效的存儲與查詢是系統完善和優化的方向,而資料庫的穩定性、可靠性是實現的基礎。高可用和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.bson
2018-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前,先將完整備份完結刪除)進行還原。
本文版權歸作者所有,未經作者同意不得轉載,謝謝配合!!!
本文版權歸作者所有,未經作者同意不得轉載,謝謝配合!!!
本文版權歸作者所有,未經作者同意不得轉載,謝謝配合!!!