1. 程式人生 > >挖一挖MongoDB的備份與還原

挖一挖MongoDB的備份與還原

一  研究背景需求

目前作者所在公司的MongoDB資料庫是每天凌晨做一次全庫完整備份,但資料庫出現故障時,只能保證恢復到全備時間點,比如,00:30 做的完整備份,而出現故障是下午18:00,那麼現有的備份機制只可以恢復到00:30,即丟失00:30 – 18:00 的操作資料。

此外,我們現在的副本集沒有delay節點,當出現誤操作或需要恢復到指定時間點操作時,目前災備機制也不支援此操作。上線這種備份方案,心裡總是惶惶的。

並且細究mongodump機制原理,此命令在執行過程中並不會把資料庫鎖死(或建立快照,以保證整個庫凍結在一個固定的時間點),實現資料庫完整性,而是細化到集合級別,如此,會導致資料完整性問題。 例如,集合A中存放了訂單概要資訊,集合B中存放了訂單的所有明細,那麼只有一個訂單有完整的明細時才是正確的狀態。那麼備份時,如果備份集合A處於時間點x,而備份集合B處於x之後的一個時間點y時,可以想象A和B中的資料極有可能不對應而失去意義(部分訂單有訂單明細而沒有訂單概要資訊)。

二  原理分析

關係型資料庫,例如MySQL ,SQL Server 都有事務日誌(或bin log),會將資料庫的DML  DDL、DCL等操作記錄在事務檔案中,可以通過日誌備份搭建還原體系。MongoDB沒有此類機制和資料檔案,難以實現。

但MongoDB 副本集 有通過 oplog(主要記錄在local資料庫oplog.rs集合中) 實現節點間的同步,此集合記錄了資料庫的OP操作,記錄的是整個mongod例項一段時間內的所有變更(插入/更新/刪除)操作。

是否可以考慮通過oplog.rs集合的備份還原來解決以上問題(資料完整性;不能還原到指定時間點;時效性差。)。

值得注意的是,oplog為replica set或者master/slave模式專用,standalone模式執行mongodb並不推薦。

檢視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

三 驗證測試

3.1 場景1  備份還原後,如何保證資料一致性、完整性

在Insert資料過程中,備份資料庫,為說明問題,資料插入跨越整個備份過程。

3.1.1 在不使用--oplog 引數下備份還原

Step 1 向資料庫ygtest041602插入資料,源庫沒有集合order0531、orderdetial

插入語句:

for(var i = 0; i < 10000; i++)

{ db.order0531.insert({a: i});};

for(i=0;i<300000;i++)

{ db.orderdetial.insert({"id":i,"name":"shenzheng","addr":"龍崗","date":new Date()}); };

step 2  在上述命令執行期間 執行mongodump 備份

./mongodump -h 172.177.XXX.XXX --port 埠 --authenticationDatabase admin -u 使用者名稱 -p 密碼 --gzip -o /data/mongodb_back/mongotestdump

Step 3 還原上述備份檔案 

./mongorestore -h 172.177.XXX.XXX --port 埠  --authenticationDatabase admin -u 使用者名稱 -p 密碼  --gzip /data/mongodb_back/mongotestdump

Step 4 檢查源庫Insert 語句, 執行完畢(一定是mongodump完畢,再insert結束;但不強調,mongorestoreinsert的時間關係)

Step 5 待語句執行完畢後,比較 源庫和 還原庫 的資料。

源庫

還原庫

我們看到集合orderdetial在兩個資料庫不一致,有差異。即這種備份還原 無法保證資料一致性。

3.1.2 增加 引數--oplog 引數下備份還原

Step 1 刪除 上次測試中遺留的集合 和 備份檔案

Step 2 啟動insert命令

for(var i = 0; i < 10000; i++)

{ db.order0531.insert({a: i});};

for(i=0;i<150000;i++)

{ db.orderdetial.insert({"id":i,"name":"shenzheng","addr":"龍崗","date":new Date()}); };

Step 3 執行備份命令

./mongodump -h 172.177.XXX.XXX --port 埠  --oplog --authenticationDatabase admin -u 使用者名稱 –p密碼  --gzip -o /data/mongodb_back/mongotestdump

從列印結果來看,有對oplog命令的輸出,檢視備份檔案 多了一個 oplog.bson 檔案

Step 4 執行還原命令,增加引數  --oplogReplay

./mongorestore -h 172.177.XXX.XXX --port 埠  --oplogReplay --authenticationDatabase admin -u 使用者名稱 –p 密碼  --gzip /data/mongodb_back/mongotestdump

從執行情況來看,此類還原命令有還原oplog

Step 5 比對資料

源庫

還原庫

本場景測試結論:

雖然結果資料仍不一致,但從後者的備份還原過程可以看出,有對oplog做單獨的處理。

資料可以保證以oplog結尾為準,實現了多集合(表)的時間一致性(MongoDB本身特性,不保證體現資料庫的事務一致性)。

3.2 場景二  還原到指定時間點

 

使用場景3.1.2 的備份檔案,測試還原到備份過程中的某個時刻。從上次備份過程可知,開始時間為2018-05-31T11:13:46.501+0800,結束時間為 2018-05-31T11:14:12.961+0800

此輪測試還原點選擇在此時間段內。時間還原點的選擇,假如我們還原到orderdetial 集合"_id" : ObjectId("5b0f6876c52291864d3485b9")的插入時的時間點。

檢視此時間點對應的時間和操作序列,在源庫local中的oplog.rs 集合查詢。

(oplog.rs集合的資料意義,我們在其他章節介紹,在此不做贅述)

"ts" : Timestamp(1527736438, 858)

此文件對應的 "id" : 9719.0,插入時間為2018-05-31T11:13:58.721+0800 【正好,在mongodump的過程中】

Step 1 刪除還原伺服器中的還原庫(上面測試遺留的資料庫)

Step 2 執行還原命令,通過--oplogLimit 引數指定時間點,時間點為上一步查到的Timestamp(1527736438, 858)

./mongorestore -h 172.177.XXX.XXX --port 埠  --oplogReplay --oplogLimit "1527736438:858" --authenticationDatabase admin -u 使用者名稱 -p 密碼  /data/mongodb_back/mongotestdump

Step 3 結果驗證

從print 出來的執行過程來看,相同的備份檔案,相同的還原環境,新增--oplogLimit "1527736438:858" 引數的 replay Oplog,要小於上次不帶--oplogLimit引數的全還原。

本次還原,新增 --oplogLimit,只還原了 824 KB 的oplog

2018-05-31T16:55:21.975+0800     replaying oplog

2018-05-31T16:55:22.724+0800     oplog  85.9KB

2018-05-31T16:55:25.720+0800     oplog  575KB

2018-05-31T16:55:27.231+0800     oplog  842KB

2018-05-31T16:55:27.231+0800     done

上次,沒有新增 --oplogLimit,預設全還原,所以還原了3.22MB oplog。

2018-05-31T11:23:33.770+0800     replaying oplog

2018-05-31T11:23:34.682+0800     oplog  146KB

2018-05-31T11:23:40.686+0800     oplog  1.10MB

2018-05-31T11:23:49.688+0800     oplog  2.61MB

2018-05-31T11:23:52.680+0800     oplog  3.11MB

2018-05-31T11:23:53.246+0800     oplog  3.22MB

2018-05-31T11:23:53.246+0800     done

這也容易理解。全還原一定大於指定時間的部分還原。

資料檢驗 :

(1)此時最大ID為"id" : 9718.0,和引數值對應的ID值連續

(2)此過程共還原資料9719,低於全還原的63862

本場景測試結論:

使用引數--oplogReplay –oplogLimit 可以將資料庫還原到指定時刻。

3.3 場景三 oplog.rs 集合中匯出資料進行還原

在場景2 中的 oplog.bson 是我們在mongodump 全庫時,新增引數--oplog引數後自動生成的 全庫備份,受限於效能損耗和資源限制,不能高頻率執行和長時間儲存。考慮到副本集的oplog儲存在oplog.rs集合中,此輪測試驗證是否只從oplog.rs匯出,就可以保證資料連續性。即從oplog.rs匯出的資料是否可以替代mongodump過程中產生的oplog.bson.

類似於我們從oplog.rs集合中匯出MySQL的binlog檔案,也類似於SQL Server Log日誌,可以實現增量備份與增量還原。

Step 1 在上述場景中繼續測試,我們已經執行全庫還原,只是時間點還原到"id" : 9718.0 對應的資料。

Step 2 刪除 上次全備過程中產生的備份檔案

Step 3 為增加試驗效果,再次在源庫插入部分資料

for(var i = 0; i < 10000; i++)

{ db.order0531.insert({a: i});};

for(i=0;i<200000;i++)

{ db.orderdetial.insert({"id":i,"name":"shenzheng","addr":"龍崗","date":new Date()}); };

step 4 將資料庫local中集合oplog.rs 匯出(建議匯出時,增加時間引數,例如一小時內的或大於某時間時刻。注意,此處的測試場景中,我們選擇的Timestamp(1527552239,1),在我們場景2中的Timestamp(1527736438, 858)的前面,故意時間重疊)

./mongodump -h 172.177.XXX.XXX --port 埠 --authenticationDatabase admin -u 使用者名稱 -p 密碼 -d local -c oplog.rs  --query '{ts:{$gte:Timestamp(1527552239,1)}}' -o /data/mongodb_back/mongotestoplog

step 5 將備份產生的檔案local/oplog.rs.bson,copy至還原路徑下,並將其命名為oplog.bson

cp ./mongotestoplog/local/oplog.rs.bson ./mongotestdump/oplog.bson

step 6 執行還原命令

./mongorestore -h 172.177.XXX.XXX --port 埠  --oplogReplay --authenticationDatabase admin -u 使用者名稱 –p 密碼  /data/mongodb_back/mongotestdump

step 7 資料驗證

還原庫

源庫

兩者資料一致

本場景測試結論:

(1)dumprestor 命令,可以接受從別處而來,除了--oplog之外,可人為獲取的oplog;還原時需重新命名(step 5)。

(2)可以實現類似關係型資料庫的增量備份與增量還原

(3)oplog有一個非常重要的特性——冪等性(idempotent)。即對一個數據集合,使用oplog中記錄的操作重放時,無論被重放多少次,其結果會是一樣的。舉例來說,如果oplog中記錄的是一個插入操作,並不會因為你重放了兩次,資料庫中就得到兩條相同的記錄。

四 總結 

1. MongoDB 不支援事務,無法保證備份還原命令中的事務完整性、業務一致性(無關係數型據庫中基於事務日誌的重做還原機制)。但結合命令引數--oplog,可以實現資料、業務的時間一致性。

2. 資料庫還原時,結合引數--oplogReplay  --oplogLimit實現指定時間點的還原。

3. 搭建副本集的MongoDB,定期匯出oplog.rs,可以實現增量備份與增量還原。