1.MongoDB 2.7主從複製(master –> slave)環境基於時間點的恢復

Snipaste_2020-08-17_18-37-33


(一)MongoDB恢復概述
對於任何類型的數據庫,若是要將數據庫恢復到過去的任意時間點,否須要有過去某個時間點的全備+全備以後的重作日誌,MongoDB也不例外。使用全備將數據庫恢復到固定時刻,而後使用重作日誌追加全備以後的操做。
mongodb

clipboard

重作日誌備份:MongoDB只有開啓主從複製或者副本集時纔會開啓重作日誌,主從複製存放在local數據庫下的oplog.$main集合中,複製集的日誌存放在local數據庫下的oplog.rs集合中,該集合是一個上限集合,當達到固定大小時,最老的記錄會被自動覆蓋。所以須要注意,MongoDB的重作日誌並不會一直保存着,可否恢復到故障點,徹底取決於日誌是否完整。數據庫


(二)操做日誌oplog
(2.1)oplog日誌格式解析
爲了查看oplog日誌保存了什麼信息,向test集合中插入2條數據:json

db.test.insert({"empno":1,"ename":"lijiaman","age":22,"address":"yunnan,kungming"});
db.test.insert({"empno":2,"ename":"aaa","age":18,"address":"sichuan,chengdu"});

查看test集合的數據信息
app

db.test.find()
 /* 1 */
 {
     "_id" : ObjectId("5f30eb58bcefe5270574cd54"),
     "empno" : 1.0,
     "ename" : "lijiaman",
     "age" : 22.0,
     "address" : "yunnan,kungming"
 }

/* 2 */
 {
     "_id" : ObjectId("5f30eb58bcefe5270574cd55"),
     "empno" : 2.0,
     "ename" : "aaa",
     "age" : 18.0,
     "address" : "sichuan,chengdu"
 }

使用下面查詢語句查看oplog日誌信息:測試

use local
var since = Math.floor(ISODate("2020-08-10T14:00:00.000Z").getTime() / 1000) - 8*60*60
var until = Math.floor(ISODate("2020-08-10T23:00:00.000Z").getTime() / 1000) - 8*60*60
db.oplog.$main.find(
 {
      $and : [
       {"ns" : /lijiamandb.test/},
       {"ts" : { "$gt" : Timestamp(since, 1),"$lt":Timestamp(until,1)}}
      ]
   }
 ).sort({ts:1})

結果以下:
spa

/* 1 */
 {
     "ts" : Timestamp(1597070283, 1),
     "op" : "i",
     "ns" : "lijiamandb.test",
     "o" : {
         "_id" : ObjectId("5f30eb58bcefe5270574cd54"),
         "empno" : 1.0,
         "ename" : "lijiaman",
         "age" : 22.0,
         "address" : "yunnan,kungming"
     }
 }

/* 2 */
 {
     "ts" : Timestamp(1597070283, 2),
     "op" : "i",
     "ns" : "lijiamandb.test",
     "o" : {
         "_id" : ObjectId("5f30eb58bcefe5270574cd55"),
         "empno" : 2.0,
         "ename" : "aaa",
         "age" : 18.0,
         "address" : "sichuan,chengdu"
     }
 }
oplog中各個字段的含義:
ts:數據寫的時間,括號裏面第1位數據表明時間戳,是自unix紀元以來的秒值,第2位表明在1s內訂購時間戳的序列數
op:操做類型,可選參數有:
       -- "i": insert
       --"u": update
       --"d": delete
       --"c": db cmd
       --"db":聲明當前數據庫 (其中ns 被設置成爲=>數據庫名稱+ '.')
       --"n": no op,即空操做,其會按期執行以確保時效性
ns:命名空間,一般是具體的集合
o:具體的寫入信息
o2: 在執行更新操做時的where條件,僅限於update時纔有該屬性

所以,若是要實現MongoDB基於時間點的恢復,只要解析oplog日誌,就能夠實現操做重作。


(2.2)確認日誌保存狀況
oplog是一個上限集合,當數據量達到必定大小後,MongoDB會自動清理oplog日誌信息,爲了保證恢復可以正常進行,須要確認日誌的時間是否符合還原需求。簡單來講,oplog應該保存着自上一次備份以來的全部日誌。可使用下面2種方法來確認最先的oplog。
方法一:查詢oplog中的最小時間3d

db.oplog.$main.aggregate([{$group:{_id:1,min_salary:{$min:"$ts"}}}])

/* 1 */
{
    "_id" : 1.0,
    "min_salary" : Timestamp(1595503517, 2)
}


方法二:查看主從複製信息
在主節點查看日誌信息,能夠看到oplog日誌大小,由於oplog是一個固定大小的集合,因此還能夠看到日誌的開始、結束時間、oplog的時間差等。unix

> db.printReplicationInfo()
configured oplog size:   2129.547656059265MB
 log length start to end: 9180secs (2.55hrs)
 oplog first event time:  Thu Jun 18 2020 21:43:14 GMT+0800 (CST)
 oplog last event time:   Fri Jun 19 2020 00:16:14 GMT+0800 (CST)
 now:                     Mon Aug 10 2020 18:59:23 GMT+0800 (CST)


(2.3)備份oplog日誌
在使用mongodump備份數據庫時,默認是不備份oplog的,須要咱們手動去備份,經常使用的備份方法以下。
(1)備份全部數據庫的oplog日誌rest

mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection='oplog.$main' --out=/root/backup/oplog


(2)備份單個數據庫的oplog日誌。例如,備份catdb數據庫的oplog日誌日誌

mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection='oplog.$main' --query='{"ns":/catdb/}' --out=/root/backup/oplog


(3)備份單個集合的oplog日誌。例如,備份catdb.myc1集合的oplog日誌

mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection='oplog.$main' --query='{"ns":"catdb.myc1"}' --out=/root/backup/oplog


(4)使用多個條件來過濾oplog日誌

# 備份catdb數據庫,且只備份insert操做的oplog日誌
mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection='oplog.$main' --query='{"ns":/catdb/,"op":"i"}' --out=/root/backup/oplog

# 備份catdb數據庫,且備份在時間Timestamp( 1597241858, 1 )到 Timestamp( 1597242471, 1 ) 之間的數據
# 須要注意,不包含上下限時間
mongodump --authenticationDatabase admin -uroot -p123456 --db=local --collection='oplog.$main' --query='{"ns":/catdb/,"ts" : { "$gt" : Timestamp( 1597241858, 1 ),"$lt":Timestamp(1597242471, 1 )}}' --out=/root/backup/oplog


(三)模擬將MongoDB恢復到任意時間點
(3.1)案例一:將整個實例恢復到某個時間點
(3.3.1)故障場景描述
業務人員發現多個MongoDB數據庫均存在數據錯誤的狀況,須要將所有數據恢復到過去的某個時刻。


(3.3.2)數據恢復方法描述
只要肯定了恢復時間點,就可使用徹底備份+oplog備份,將數據恢復到過去的某個時刻。


(3.3.3)恢復過程
STEP1:模擬業務正常運行,數據正常進入MongoDB數據庫

use db1
db.db1test.insert({id:1,name:'a'})
db.db1test.insert({id:2,name:'b'})

use db2
db.db2test.insert({id:11,name:'aa'})
db.db2test.insert({id:22,name:'bb'})

STEP2:執行完整備份

mongodump --authenticationDatabase admin -uroot -p123456 -o /root/backup/full

STEP3:再次模擬業務正常運行,數據正常進入MongoDB數據庫

use db1
db.db1test.insert({id:3,name:'c'})

use db2
db.db2test.insert({id:33,name:'cc'})

最終數據以下:

> use db1
 switched to db db1
 > db.db1test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26862"), "id" : 1, "name" : "a" }
 { "_id" : ObjectId("5f35110ba27e9a00c0f26863"), "id" : 2, "name" : "b" }
 { "_id" : ObjectId("5f35113ca27e9a00c0f26866"), "id" : 3, "name" : "c" }
 > 
 > 
 > use db2
 switched to db db2
 > db.db2test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26864"), "id" : 11, "name" : "aa" }
 { "_id" : ObjectId("5f35110ca27e9a00c0f26865"), "id" : 22, "name" : "bb" }
 { "_id" : ObjectId("5f35113da27e9a00c0f26867"), "id" : 33, "name" : "cc" }
 >

STEP4:模擬數據誤操做

# db1的db1test集合id增長100
use db1
db.db1test.update({},{$inc:{"id":100}},{multi:true})

# db2的db2test集合被刪除
use db2
db.db2test.drop()

錯誤操做以後的結果:

> use db1
 switched to db db1
 > db.db1test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26862"), "id" : 101, "name" : "a" }
 { "_id" : ObjectId("5f35110ba27e9a00c0f26863"), "id" : 102, "name" : "b" }
 { "_id" : ObjectId("5f35113ca27e9a00c0f26866"), "id" : 103, "name" : "c" }
 > 
 > use db2
 switched to db db2
 > db.db2test.find()
 >

要求把全部數據庫的數據恢復到STEP4以前的狀態。

STEP5:中止業務,再也不往數據庫寫數據

STEP6:備份日誌。能夠備份部分日誌,也能夠備份所有日誌

mongodump --authenticationDatabase admin -uroot -p123456 -d local -c 'oplog.$main' -o /root/backup/oplog/

STEP7:確認數據異常時間點,對oplog集合進行分析

use local
 db.oplog.$main.find(
  {
      $and : [
       {"ns" : /db1/},
       {"op" : "u" }
      ]
   }
 ).sort({ts:1})

查詢結果以下,能夠確認,開始對db1.db1test集合更新的時間爲Timestamp(1597313442, 1)

/* 1 */
 {
    "ts" : Timestamp(1597313442, 1),
     "op" : "u",
     "ns" : "db1.db1test",
     "o2" : {
         "_id" : ObjectId("5f35110ba27e9a00c0f26862")
     },
     "o" : {
         "$set" : {
             "id" : 101.0
         }
     }
 }

/* 2 */
 {
     "ts" : Timestamp(1597313442, 2),
     "op" : "u",
     "ns" : "db1.db1test",
     "o2" : {
         "_id" : ObjectId("5f35110ba27e9a00c0f26863")
     },
     "o" : {
         "$set" : {
             "id" : 102.0
         }
     }
 }

/* 3 */
 {
     "ts" : Timestamp(1597313442, 3),
     "op" : "u",
     "ns" : "db1.db1test",
     "o2" : {
         "_id" : ObjectId("5f35113ca27e9a00c0f26866")
     },
     "o" : {
         "$set" : {
             "id" : 103.0
         }
     }
 }


STEP8:執行徹底備份的恢復
須要注意,考慮是否須要使用"--drop"選項,若是不用該選項,會保留集合中當前的數據,若是使用了drop選項,在導入集合時會先刪除集合。這裏使用該選項

mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --drop  /root/backup/full/

須要注意權限問題,這裏發現使用root帳號沒法執行恢復,可是使用權限較小的root2帳號卻能夠(備註:關於root和root2用戶權限信息,會在文檔結尾給出):
[root@mongo1 oplog]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --drop  /root/backup/full/
connected to: 127.0.0.1:27017
2020-08-13T10:25:05.963+0000     going into namespace [admin.system.version]
1 document found
2020-08-13T10:25:05.964+0000     Creating index: { key: { _id: 1 }, name: "_id_", ns: "admin.system.version" }
Error creating index admin.system.version: 13 err: "not authorized to create index on admin.system.version"
Aborted

[root@mongo1 full]# mongorestore --authenticationDatabase admin -uroot2 -p123456 --port=27017 --drop  /root/backup/full/

確認全量恢復的數據,已經恢復回來:

> use db1
switched to db db1
 > db.db1test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26862"), "id" : 1, "name" : "a" }
 { "_id" : ObjectId("5f35110ba27e9a00c0f26863"), "id" : 2, "name" : "b" }
 > 
 > 
 > 
 > use db2
 switched to db db2
 > db.db2test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26864"), "id" : 11, "name" : "aa" }
 { "_id" : ObjectId("5f35110ca27e9a00c0f26865"), "id" : 22, "name" : "bb" }
 >

STEP9:使用oplog執行增量恢復
在恢復oplog以前,須要對其格式進行處理,不然會報錯:

# 報錯提示找不到oplog
 [root@mongo1 full]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --oplogReplay --oplogLimit "1597313442:1" /root/backup/oplog/local
 connected to: 127.0.0.1:27017
 No oplog file to replay. Make sure you run mongodump with --oplog.

須要把oplog.$main.metadata.json 文件刪除,把oplog.$main.bson名字改成oplog.bson

[root@mongo1 local]# pwd
/root/backup/oplog/local
[root@mongo1 local]# ls
oplog.$main.bson  oplog.$main.metadata.json
[root@mongo1 local]# rm -rf oplog.\$main.metadata.json 
[root@mongo1 local]# mv oplog.\$main.bson oplog.bson 
[root@mongo1 local]# ls
oplog.bson

最後執行oplog增量恢復便可

mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --oplogReplay --oplogLimit "1597313442:1" /root/backup/oplog/local

注意:這裏有一個大坑,須要特別留意,在使用上述命令導入數據時,整個過程沒有報錯,可是最終數據並無恢復回來,以下面所示:
整個導入過程沒有報錯

[root@mongo1 local]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 --oplogReplay --oplogLimit "1597313442:1" /root/backup/oplog/local
 connected to: 127.0.0.1:27017
 2020-08-13T10:33:27.830+0000      Replaying oplog
 2020-08-13T10:33:30.013+0000         Progress: 3055430/1353998783    0%    (bytes)
 2020-08-13T10:33:33.005+0000         Progress: 5632309/1353998783    0%    (bytes)
 2020-08-13T10:33:36.003+0000         Progress: 8604531/1353998783    0%    (bytes)
 ...
 ...
 2020-08-13T10:44:07.009+0000         Progress: 1340889939/1353998783    99%    (bytes)
 2020-08-13T10:44:10.004+0000         Progress: 1351348749/1353998783    99%    (bytes)
 4604171 documents found
 2020-08-13T10:44:10.699+0000 Applied 4592833 oplog entries out of 4592837 (4 skipped).

然而數據未恢復回來

> use db1
 switched to db db1
 > db.db1test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26862"), "id" : 1, "name" : "a" }
 { "_id" : ObjectId("5f35110ba27e9a00c0f26863"), "id" : 2, "name" : "b" }
 > 
 > use db2
 switched to db db2
 > db.db2test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26864"), "id" : 11, "name" : "aa" }
 { "_id" : ObjectId("5f35110ca27e9a00c0f26865"), "id" : 22, "name" : "bb" }

查詢error log日誌,發現root用戶沒有權限執行導入

2020-08-13T10:44:10.692+0000 [conn18] Unauthorized not authorized on db1 to execute command { applyOps: [ { ts: Timestamp 1597313291000|1, op: "i", ns: "db1.db1test", o: { _id: ObjectId('5f35110ba27e9a00c0f26862'), id: 1.0, name: "a" } } ] }
 2020-08-13T10:44:10.692+0000 [conn18] Unauthorized not authorized on db1 to execute command { applyOps: [ { ts: Timestamp 1597313291000|2, op: "i", ns: "db1.db1test", o: { _id: ObjectId('5f35110ba27e9a00c0f26863'), id: 2.0, name: "b" } } ] }
 2020-08-13T10:44:10.692+0000 [conn18] Unauthorized not authorized on db2 to execute command { applyOps: [ { ts: Timestamp 1597313291000|3, op: "i", ns: "db2.db2test", o: { _id: ObjectId('5f35110ba27e9a00c0f26864'), id: 11.0, name: "aa" } } ] }
 2020-08-13T10:44:10.692+0000 [conn18] Unauthorized not authorized on db2 to execute command { applyOps: [ { ts: Timestamp 1597313292000|1, op: "i", ns: "db2.db2test", o: { _id: ObjectId('5f35110ca27e9a00c0f26865'), id: 22.0, name: "bb" } } ] }
 2020-08-13T10:44:10.692+0000 [conn18] Unauthorized not authorized on db1 to execute command { applyOps: [ { ts: Timestamp 1597313340000|1, op: "i", ns: "db1.db1test", o: { _id: ObjectId('5f35113ca27e9a00c0f26866'), id: 3.0, name: "c" } } ] }
 2020-08-13T10:44:10.692+0000 [conn18] Unauthorized not authorized on db2 to execute command { applyOps: [ { ts: Timestamp 1597313341000|1, op: "i", ns: "db2.db2test", o: { _id: ObjectId('5f35113da27e9a00c0f26867'), id: 33.0, name: "cc" } } ] }

處理辦法:使用root2用戶導入

[root@mongo1 local]# mongorestore --authenticationDatabase admin -uroot2 -p123456 --port=27017 --oplogReplay --oplogLimit "1597313442:1" /root/backup/oplog/local

STEP10:確認數據恢復狀況,發現數據以及恢復到了STEP4以前的狀態

> use db1
 switched to db db1
 > db.db1test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26862"), "id" : 1, "name" : "a" }
 { "_id" : ObjectId("5f35110ba27e9a00c0f26863"), "id" : 2, "name" : "b" }
 { "_id" : ObjectId("5f35113ca27e9a00c0f26866"), "id" : 3, "name" : "c" }
 > 
 > 
 > use db2
 switched to db db2
 > db.db2test.find()
 { "_id" : ObjectId("5f35110ba27e9a00c0f26864"), "id" : 11, "name" : "aa" }
 { "_id" : ObjectId("5f35110ca27e9a00c0f26865"), "id" : 22, "name" : "bb" }
 { "_id" : ObjectId("5f35113da27e9a00c0f26867"), "id" : 33, "name" : "cc" }
 >
至此恢復結束。



(3.2)案例二:誤刪除某個DB,對單個DB進行恢復
一般,每一個DB承載不一樣的業務,相互之間沒有關係,若是出現故障,每每會表如今某個DB上,所以,若是出現故障,只對相應的DB進行恢復,那將減少對業務的影響。
(3.2.1)故障場景描述
假設業務運行過程當中,數據庫db3被人誤刪除了,咱們須要對db3進行恢復,而且不能影響到其它的DB業務。


(3.2.2)數據恢復方法描述
能夠在當前實例上進行恢復,也能夠新啓動一個mongod實例,用於數據恢復,而後再把確認無誤的數據導入到生產環境中,咱們採用新的mongod實例來恢復數據。
1.首先新啓動一個mongod實例;
2.將已有的徹底備份恢復到新的實例上;
3.備份oplog,只備份db3的oplog,其它數據庫的不備份;
4.使用oplog將數據庫恢復到刪除以前;
5.檢查db3數據庫的數據,確認是否恢復回來;
6.若是第5步沒有問題,mongodump導出db3數據庫,而後倒入到生產環境中。


(3.2.3)恢復過程
STEP1:模擬業務正常運行,數據正常進入MongoDB數據庫

use db3
db.db3test.insert({id:111,name:'aaa'})
db.db3test.insert({id:222,name:'bbb'})
db.db3test.insert({id:333,name:'ccc'})

STEP2:執行完整備份

mongodump --authenticationDatabase admin -uroot -p123456 -o /root/backup/full

STEP3:再次模擬業務正常運行,數據正常進入MongoDB數據庫

use db3
db.db3test.insert({id:444,name:'ddd'})
db.db3test.insert({id:555,name:'eee'})
db.db3test.insert({id:666,name:'fff'})

最終數據以下:

> db.db3test.find()
 { "_id" : ObjectId("5f352400a27e9a00c0f2686b"), "id" : 111, "name" : "aaa" }
 { "_id" : ObjectId("5f352400a27e9a00c0f2686c"), "id" : 222, "name" : "bbb" }
 { "_id" : ObjectId("5f352401a27e9a00c0f2686d"), "id" : 333, "name" : "ccc" }
 { "_id" : ObjectId("5f352428a27e9a00c0f2686e"), "id" : 444, "name" : "ddd" }
 { "_id" : ObjectId("5f352428a27e9a00c0f2686f"), "id" : 555, "name" : "eee" }
 { "_id" : ObjectId("5f352429a27e9a00c0f26870"), "id" : 666, "name" : "fff" }

STEP4:模擬數據誤操做

> db
db3

> db.dropDatabase()
{ "dropped" : "db3", "ok" : 1 }

接下來執行恢復操做。

STEP5在發現誤操做以後,咱們須要把db3恢復回來,首先應該備份oplog,這裏只涉及到db3數據庫,只要備份db3的oplog便可,這樣能夠加快備份恢復速度

mongodump --authenticationDatabase admin -uroot -p123456 -d local -c 'oplog.$main' -q '{"ns":/db3/}'  -o /root/backup/oplog/

STEP6:從新開啓一個mongod實例

mongod --port=27018 --dbpath=/tmp/data

STEP7:在新的實例上恢復全備數據,只要恢復db3便可

mongorestore  --port=27018 -d db3 /root/backup/full/db3

確認數據全備恢復狀況

# 執行恢復前
> show dbs
 admin  (empty)
 local  0.078GB
> 

# 執行恢復後,db3數據已經恢復到了全備時的狀態
> show dbs
 admin  (empty)
 db3    0.078GB
 local  0.078GB
 > 
 > 
 > use db3
 switched to db db3
 > show collections
 db3test
 system.indexes
 > db.db3test.find()
 { "_id" : ObjectId("5f352400a27e9a00c0f2686b"), "id" : 111, "name" : "aaa" }
 { "_id" : ObjectId("5f352400a27e9a00c0f2686c"), "id" : 222, "name" : "bbb" }
 { "_id" : ObjectId("5f352401a27e9a00c0f2686d"), "id" : 333, "name" : "ccc" }
 >

STEP8:在新的實例上恢復oplog數據,恢復到drop操做以前
先確認drop db3數據庫的時間點:  "ts" : Timestamp(1597318247, 1)

use local
 db.oplog.$main.find(
  {
      $and : [
       {"ns" : /db3/},
       {"op" : "c" }
      ]
   }
 ).sort({ts:1})


 // 結果
{
     "ts" : Timestamp(1597318247, 1),
     "op" : "c",
     "ns" : "db3.$cmd",
     "o" : {
         "dropDatabase" : 1.0
     }
 }

執行增量恢復:

# 先處理oplog,刪除文件oplog.$main.metadata.json,修改oplog.$main.bson爲oplog.bson
 [root@mongo1 local]# pwd
 /root/backup/oplog/local
 [root@mongo1 local]# rm -f oplog.\$main.metadata.json 
 [root@mongo1 local]# mv oplog.\$main.bson oplog.bson
 [root@mongo1 local]# ls
 oplog.bson

# 執行恢復
mongorestore --port=27018 --oplogReplay --oplogLimit "1597318247:1" /root/backup/oplog/local

檢查數據是否已經恢復,能夠確認,數據已經恢復回來

> db.db3test.find()
 { "_id" : ObjectId("5f352400a27e9a00c0f2686b"), "id" : 111, "name" : "aaa" }
 { "_id" : ObjectId("5f352400a27e9a00c0f2686c"), "id" : 222, "name" : "bbb" }
 { "_id" : ObjectId("5f352401a27e9a00c0f2686d"), "id" : 333, "name" : "ccc" }
 { "_id" : ObjectId("5f352428a27e9a00c0f2686e"), "id" : 444, "name" : "ddd" }
 { "_id" : ObjectId("5f352428a27e9a00c0f2686f"), "id" : 555, "name" : "eee" }
 { "_id" : ObjectId("5f352429a27e9a00c0f26870"), "id" : 666, "name" : "fff" }

STEP9:把數據導出再導入到生產環境

# 重新的mongod環境導出db3數據庫
[root@mongo1 ~]# mongodump -d db3 --port=27018 -out=/root

# 將db3導入到生產環境,這裏須要考慮是否用--drop關鍵字
[root@mongo1 ~]# mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 -d db3 /root/db3

確認數據是否已經導入到生產環境:

> show dbs
 admin       0.078GB
 catdb       0.078GB
 db1         0.078GB
 db2         0.078GB
 db3         0.078GB
 dogdb       0.078GB
 lijiamandb  0.078GB
 local       4.076GB
 mydb        0.078GB
 testdb      0.078GB
 > 
 > use db3
 switched to db db3
 > show collections
 db3test
 system.indexes
 > 
 > db.db3test.find()
 { "_id" : ObjectId("5f352400a27e9a00c0f2686b"), "id" : 111, "name" : "aaa" }
 { "_id" : ObjectId("5f352400a27e9a00c0f2686c"), "id" : 222, "name" : "bbb" }
 { "_id" : ObjectId("5f352401a27e9a00c0f2686d"), "id" : 333, "name" : "ccc" }
 { "_id" : ObjectId("5f352428a27e9a00c0f2686e"), "id" : 444, "name" : "ddd" }
 { "_id" : ObjectId("5f352428a27e9a00c0f2686f"), "id" : 555, "name" : "eee" }
 { "_id" : ObjectId("5f352429a27e9a00c0f26870"), "id" : 666, "name" : "fff" }
 >

數據以及所有導入到了生產環境,測試完成。注意,別忘記關閉新建的mongod實例。




(3.3)案例三:誤操做某個集合,對單個集合進行恢復
(3.3.1)故障場景描述
業務人員執行誤刪操DBA對數據進行恢復,詳細過程以下:
T1~T2:業務正常運行,數據正常進入數據庫
T2:使用mongodump執行數據庫徹底備份
T2~T4:業務正常運行,數據正常進入數據庫
T4:用戶誤刪除數據
T4~T6:業務還在運行,可是已經出現問題,如此時還能正常插入數據,可是查詢、更新、刪除數據存在找不到數據的錯誤
T6:DBA介入數據恢復

clipboard


(3.3.2)數據恢復方法描述
能夠在當前實例上進行恢復,也能夠新啓動一個mongod實例,用於數據恢復,咱們在上一個例子中已經使用新建mongod實例的方式來恢復數據,本次實驗咱們直接在生產實例上進行恢復。
1.執行徹底恢復,使用徹底備份,將數據庫恢復到T2時刻;
2.找到T4時刻故障以前的時間,從而肯定T2~T4之間的oplog日誌。結合T2時刻的全備+ T2~T4之間的oplog日誌,實現數據恢復;(備註:這裏不須要去確認T2以後的日誌開始時間,在使用oplog恢復數據時,是經過惟一編號「_id」來操做數據的,oplog可能從全備份以前的任意時間開始,可是並不影響數據的正確性)。
3.找到T4時刻故障以後的時間,備份oplog。
4.使用oplog,實現T4~T6時間段的恢復。


(3.3.3)恢復過程
STEP1:模擬業務正常運行,數據正常進入MongoDB數據庫

use db4
db.db4test.insert({id:1111,name:'aaaa'})
db.db4test.insert({id:2222,name:'bbbb'})
db.db4test.insert({id:3333,name:'cccc'})

STEP2:執行完整備份

mongodump --authenticationDatabase admin -uroot -p123456 -o /root/backup/full

STEP3:再次模擬業務正常運行,數據正常進入MongoDB數據庫

use db4
db.db4test.insert({id:4444,name:'dddd'})
db.db4test.insert({id:5555,name:'eeee'})
db.db4test.insert({id:6666,name:'ffff'})

最終數據以下:

> db.db4test.find()
{ "_id" : ObjectId("5f3545c3a27e9a00c0f26871"), "id" : 1111, "name" : "aaaa" }
{ "_id" : ObjectId("5f3545c3a27e9a00c0f26872"), "id" : 2222, "name" : "bbbb" }
{ "_id" : ObjectId("5f3545c4a27e9a00c0f26873"), "id" : 3333, "name" : "cccc" }
{ "_id" : ObjectId("5f354631a27e9a00c0f26874"), "id" : 4444, "name" : "dddd" }
{ "_id" : ObjectId("5f354631a27e9a00c0f26875"), "id" : 5555, "name" : "eeee" }
{ "_id" : ObjectId("5f354632a27e9a00c0f26876"), "id" : 6666, "name" : "ffff" }

STEP4:模擬數據誤操做,刪除2條數據

> db.db4test.remove({id:{$gt:4444}})
 WriteResult({ "nRemoved" : 2 })
 > 
 > db.db4test.find()
 { "_id" : ObjectId("5f3545c3a27e9a00c0f26871"), "id" : 1111, "name" : "aaaa" }
 { "_id" : ObjectId("5f3545c3a27e9a00c0f26872"), "id" : 2222, "name" : "bbbb" }
 { "_id" : ObjectId("5f3545c4a27e9a00c0f26873"), "id" : 3333, "name" : "cccc" }
 { "_id" : ObjectId("5f354631a27e9a00c0f26874"), "id" : 4444, "name" : "dddd" }

STEP5:再次模擬業務正常運行,數據正常進入MongoDB數據庫

use db4
db.db4test.insert({id:7777,name:'gggg'})
db.db4test.insert({id:8888,name:'hhhh'})
db.db4test.insert({id:9999,name:'kkkk'})

最終數據以下:

> db.db4test.find()
 { "_id" : ObjectId("5f3545c3a27e9a00c0f26871"), "id" : 1111, "name" : "aaaa" }
 { "_id" : ObjectId("5f3545c3a27e9a00c0f26872"), "id" : 2222, "name" : "bbbb" }
 { "_id" : ObjectId("5f3545c4a27e9a00c0f26873"), "id" : 3333, "name" : "cccc" }
 { "_id" : ObjectId("5f354631a27e9a00c0f26874"), "id" : 4444, "name" : "dddd" }
 { "_id" : ObjectId("5f3546ada27e9a00c0f26877"), "id" : 7777, "name" : "gggg" }
 { "_id" : ObjectId("5f3546ada27e9a00c0f26878"), "id" : 8888, "name" : "hhhh" }
 { "_id" : ObjectId("5f3546ada27e9a00c0f26879"), "id" : 9999, "name" : "kkkk" }

此時,咱們發現id爲5555和6666的數據是被誤刪除的,須要恢復回來,而且要保留執行刪除命令以後的數據。

STEP6:在發現誤操做以後,首先應該備份oplog,這裏只涉及到db4.db4test集合,只要備份該集合的oplog便可,這樣能夠加快備份恢復速度

mongodump --authenticationDatabase admin -uroot -p123456 -d local -c 'oplog.$main' -q '{"ns":"db4.db4test"}'  -o /root/backup/oplog/

STEP7:對該集合執行徹底恢復操做

mongorestore --authenticationDatabase admin -uroot -p123456 --port=27017 -d db4 -c db4test  /root/backup/full/db4/db4test.bson

STEP8:使用oplog,對該集合執行增量恢復操做
先查看對db4.db4test集合執行刪除的開始時間

use local
 db.oplog.$main.find(
  {
      $and : [
       {"ns" : /db4.db4test/},
       {"op" : "d" }
      ]
   }
 ).sort({ts:1})


 // 結果
/* 1 */
 {
     "ts" : Timestamp(1597326944, 1),
     "op" : "d",
     "ns" : "db4.db4test",
     "b" : true,
     "o" : {
         "_id" : ObjectId("5f354631a27e9a00c0f26875")
     }
 }

/* 2 */
 {
     "ts" : Timestamp(1597326944, 2),
     "op" : "d",
     "ns" : "db4.db4test",
     "b" : true,
     "o" : {
         "_id" : ObjectId("5f354632a27e9a00c0f26876")
     }
 }

能夠看到,刪除的開始時間爲:Timestamp(1597326944, 1)。
執行增量恢復:

# 先處理oplog,刪除文件oplog.$main.metadata.json,修改oplog.$main.bson爲oplog.bson
[root@mongo1 local]# pwd
 /root/backup/oplog/local
 [root@mongo1 local]# rm -f oplog.\$main.metadata.json 
 [root@mongo1 local]# mv oplog.\$main.bson oplog.bson
 [root@mongo1 local]# ls
 oplog.bson

# 執行恢復,root用戶沒權限導入,root2用戶纔有權限
mongorestore --authenticationDatabase admin -uroot2 -p123456 --port=27017  --oplogReplay --oplogLimit "1597326944:1" /root/backup/oplog/local

STEP9:查看數據是否恢復,確認已經徹底恢復回來

> db.db4test.find()
 { "_id" : ObjectId("5f3545c3a27e9a00c0f26872"), "id" : 2222, "name" : "bbbb" }
 { "_id" : ObjectId("5f3545c4a27e9a00c0f26873"), "id" : 3333, "name" : "cccc" }
 { "_id" : ObjectId("5f354631a27e9a00c0f26874"), "id" : 4444, "name" : "dddd" }
 { "_id" : ObjectId("5f3546ada27e9a00c0f26877"), "id" : 7777, "name" : "gggg" }
 { "_id" : ObjectId("5f3546ada27e9a00c0f26878"), "id" : 8888, "name" : "hhhh" }
 { "_id" : ObjectId("5f3546ada27e9a00c0f26879"), "id" : 9999, "name" : "kkkk" }
 { "_id" : ObjectId("5f3545c3a27e9a00c0f26871"), "id" : 1111, "name" : "aaaa" }
 { "_id" : ObjectId("5f354631a27e9a00c0f26875"), "id" : 5555, "name" : "eeee" }
 { "_id" : ObjectId("5f354632a27e9a00c0f26876"), "id" : 6666, "name" : "ffff" }
到此,MongoDB 2.7主從複製環境基於時間點恢復已經測試完成。




補 充:用戶root和root2權限信息                                                    

目前在導入數據時,使用具備root權限的超級用戶進行數據導入,發現依然存在權限不走的提示。通過stackoverflow上面的提示,建立了root2用戶來導入數據,再也不報錯。

stackoverflow:https://stackoverflow.com/questions/55208028/mongodb-applyops-not-authorized-on-admin-to-execute-command


root用戶權限信息以下:具備userAdminAnyDatabase和root角色

> db.getUser("root")
{
     "_id" : "admin.root",
     "user" : "root",
     "db" : "admin",
     "roles" : [
         {
             "role" : "userAdminAnyDatabase",
             "db" : "admin"
         },
         {
             "role" : "root",
             "db" : "admin"
         }
     ]
 }

root2用戶權限信息以下,這裏直接給出建立角色和用戶的腳本

db.createRole(
    {
      role: "interalUseOnlyOplogRestore",
      privileges: [
        { resource: { anyResource: true }, actions: [ "anyAction" ] }
      ],
      roles: []
    }
 )

db.createUser({
   user: "root2",
   pwd: "123456",
  roles: [
     "interalUseOnlyOplogRestore"
   ]
 })


【完】

相關文章
相關標籤/搜索