天天看點

MongoDB學習:備份恢複(一)

本文主要介紹MongoDB副本集備份恢複的方法、常用指令,以及一些具體的操作過程,最後介紹一種備份方案。

一、備份方法

0、oplog

0.1 oplog是什麼

oplog是一個 capped collection(有上限的集合),位于local庫裡

0.2 local庫

local庫是MongoDB的系統庫,記錄着時間戳和索引和複制集等資訊

0.2.1 沒有建立副本集的時候

> show collections
startup_log           

0.2.2 建立的副本集之後(3.4、3.6和4.0都是這些集合)

replSet27017:PRIMARY> use local
switched to db local
replSet27017:PRIMARY> show collections
me       # me集合儲存了伺服器名稱                 
oplog.rs    # oplog.rs集合記錄對節點的所有操作
replset.election
replset.minvalid    #replset.minvalid集合儲存了資料庫最新操作的時間戳
replset.oplogTruncateAfterPoint
startup_log         #startup_log集合記錄這mongod每一次的啟動資訊
system.replset      #system.replset記錄着複制集的成員配置資訊,rs.conf()讀取這個集合
system.rollback.id           

0.3 檢視oplog

0.3.1 oplog字段說明

replSet27017:PRIMARY> db.oplog.rs.find().pretty().limit(1)
{
    "ts" : Timestamp(1558940829, 599),
    "t" : NumberLong(12),
    "h" : NumberLong("6800425020320237930"),
    "v" : 2,
    "op" : "i",
    "ns" : "config.system.sessions",
    "ui" : UUID("10b4d6ac-37df-4586-8a2d-83a3389d7406"),
    "wall" : ISODate("2019-05-16T08:44:39.629Z"),
    "o" : {
        "_id" : {
            "id" : UUID("fa4f2ff2-8251-4dec-b517-ebcab6db2eca"),
            "uid" : BinData(0,"LkqZkoMQbGtpaIwmLLkI3wtgnsDvRhwXsdiic1EAMLE=")
        },
        "lastUse" : ISODate("2019-05-16T08:44:39.629Z"),
        "user" : {
            "name" : "test@test01"
        }
    }           
ts: 操作時間,8位元組的時間戳,由4位元組unix timestamp + 4位元組自增計數表示(表示某一秒内的第幾次操作)
h:操作的全局唯一辨別
v:oplog版本資訊
op:1位元組的操作類型
    i:插入操作
    u:更新操作
    d:删除操作
    c:執行指令(如createDatabase,dropDatabase)
    n:空操作,會定期執行以確定時效性
ns:操作針對的集合
o:操作内容
o2:在執行更新操作時的where條件,僅限于update時才有該屬性           
#檢視oplog的資訊
replSet27017:PRIMARY> db.printReplicationInfo()
configured oplog size:   1024MB
log length start to end: 1866871secs (518.58hrs)
oplog first event time:  Tue May 14 2019 23:15:10 GMT+0800 (CST)
oplog last event time:   Wed Jun 05 2019 13:49:41 GMT+0800 (CST)
now:                     Mon May 20 2019 03:20:48 GMT+0800 (CST)

#檢視slave的同步狀态
replSet27017:PRIMARY> db.printSlaveReplicationInfo()
source: 192.168.1.12:27017
    syncedTo: Wed Jun 05 2019 13:49:41 GMT+0800 (CST)
    0 secs (0 hrs) behind the primary
source: 192.168.1.13:27017
    syncedTo: Wed Jun 05 2019 13:49:41 GMT+0800 (CST)
    0 secs (0 hrs) behind the primary           

0.3.2 根據操作類型查操作

查詢oplog裡的insert記錄,對應op為i的記錄:
replSet27017:PRIMARY> db.oplog.rs.find({"op" : "i"}).pretty().limit(3)

查update操作指令:
replSet27017:PRIMARY> db.oplog.rs.find({"op" : "u"}).pretty().limit(3)
test:PRIMARY>
查delete操作指令:
replSet27017:PRIMARY> db.oplog.rs.find({"op" : "d"}).pretty().limit(3)           

0.3.3 根據時間查詢操作

mongodb裡的時間
#作業系統時間
#date
Mon May 20 02:33:45 CST 2019

#mongo裡顯示當地時間
replSet27017:PRIMARY> Date()
Mon May 20 2019 02:34:21 GMT+0800 (CST)
#建構一個格林尼治時間,我們是東八區,是以需要減去8個小時
replSet27017:PRIMARY> new Date
ISODate("2019-05-19T18:34:24.157Z")
#也是格林尼治時間
replSet27017:PRIMARY> ISODate()
ISODate("2019-05-19T18:34:28.820Z")

#時間戳
ISODate().valueOf()
1558291032161
           

1、複制檔案

1.1 檔案系統快照

要建立一緻的備份,必須鎖定資料庫,并且必須在備份過程中暫停對資料庫的所有寫入;

快照反映了所有的資料和journal log開始的時刻,沒有增量備份。

1.1.1 使用LVM建立快照

lvcreate --size 100M --snapshot --name mdb-snap01 /dev/vg0/mdb-snap01           

--snapshot 建立一個LVM快照

--size 快照的上限大小,如果耗盡空間,快照将無法使用

1.1.2 歸檔快照

如将上述快照挂載後壓縮

umount /dev/vg0/mdb-snap01
dd if=/dev/vg0/mdb-snap01 | gzip > mdb-snap01.gz           

1.1.3 恢複快照

lvcreate --size 1G --name mdb-new vg0
gzip -d -c mdb-snap01.gz | dd of=/dev/vg0/mdb-new
mount /dev/vg0/mdb-new /srv/mongodb           

将mdb-snap01.gz 解壓并放到mdb-new裡

将mdb-new挂載到 /srv/mongodb目錄, 根據需要修改挂載點以對應MongoDB資料檔案位置

  • 注意:
    還原的快照将具有mongod.lock檔案。如果不從快照中删除此檔案,MongoDB可能會認為是正常關閉。
    如果在啟用storage.journal.enabled的情況下運作,并且不使用db.fsyncLock(),則無需删除mongod.lock檔案;如果使用db.fsyncLock(),則需要删除mongod.lock檔案。
               

1.1.4 将備份放到遠端存儲

過程與上述相同,隻是使用SSH在遠端系統上歸檔和壓縮備份。

umount /dev/vg0/mdb-snap01
dd if=/dev/vg0/mdb-snap01 | ssh [email protected] gzip > /opt/backup/mdb-snap01.gz
lvcreate --size 1G --name mdb-new vg0
ssh [email protected] gzip -d -c /opt/backup/mdb-snap01.gz | dd of=/dev/vg0/mdb-new
mount /dev/vg0/mdb-new /srv/mongodb           

1.1.5 大體過程

  • 鎖定資料庫,使用db.fsyncLock()
  • 執行上述快照備份指令
  • 快照完成,解鎖資料庫,使用db.fsyncUnlock()

1.2 直接複制檔案

相比于檔案系統快照,直接複制檔案更為簡單

1.2.1 複制資料檔案

直接cp資料檔案無法複制所有的檔案,是以需要在備份的時候防止資料檔案發生改變,做法也使用fsynclock指令。

replSet27017:PRIMARY> db.fsyncLock()           

該指令會鎖定(lock)資料庫,禁止任何寫入,并進行同步(fsync),即将所有的髒頁刷到磁盤。Mongod會将之後的所有寫操作加入等待隊列

cp -R  /data1/mongodb27017/* /backup/mongodb27017_20190704           

資料複制完成後,解鎖資料庫:

replSet27017:PRIMARY> db.fsyncUnlock();           
  •        如果啟用了身份驗證,則在調用fsyncLock()和fsynUnlock()期間不要關閉shell。如果斷開了連接配接,則可能無法進行重新連接配接,并不得不重新開機mongod。FsyncLok()的設定是在重新開機後不會保持生效,mongod總是以非鎖定模式啟動的。

當然,除了使用fsyncLock()外,關閉mongod,複制檔案,再啟動mongod,也是一種方法。

2、mongodump 與 mongorestore

2.1 mongodump

2.1.1 常用參數

-d:資料庫名
  -c:集合名
  -o:要導出的檔案名
  -q:導出資料的過濾條件
  --authenticationDatabase:儲存使用者憑證的資料庫
  --gzip:備份時壓縮
  --oplog:導出的同時生成一個oplog.bson檔案,存放在你開始進行dump到dump結束之間所有的oplog           

2.1.2常用備份操作

#備份所有庫
./mongodump -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -o /data/mongodb/backup/full_20190525
#備份test01庫
./mongodump -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d test01 -o /data/mongodb/backup/20190525
#備份test01庫下 testCollection集合
./mongodump -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d test01 -c testCollection -o /data/mongodb/backup/20190525           

2.2 mongorestore

2.2.1 常用參數

--drop:  恢複每個集合之前先删除
--oplogReplay: 重放oplog.bson中的操作内容
--oplogLimit:  與--oplogReplay一起使用時,可以限制重放到的時間點           

2.3 模拟不斷插入操作過程中備份和恢複(恢複到單節點)

模拟不斷插入集合test.table

use test1
for(var i = 0; i < 10000; i++) {
db.test.table.insert({a: i});
}           

在插入過程中mongodump備份并指定--oplog:

./mongodump -vv -h127.0.0.1:27017 -uroot -prootroot --authenticationDatabase admin --oplog -o /data/mongodb/backup           

恢複test1庫(在一個單節點恢複)

./mongorestore -h127.0.0.1:27018  -d test1 /data/backup1/test1           

檢視插入了多少條

> db.test.table.count()
4029           

應用插入期間記錄的oplog

mongorestore -vv -h127.0.0.1:27018 --oplogReplay /data/backup1

檢視應用了oplog後插入了多少條(說明這些是在備份期間插入的)

> db.test.table.count()
4044           

注意:--oplog選項隻對全庫導出有效,是以不能指定-d選項

檢視oplog.bson内容

./bsondump /data/mongodb/backup1/oplog.bson           

3、mongoexport 與 mongoimport

3.1 mongoexport

參數:

-csv:指明要導出為csv格式
-f:指明需要導的字段           

備份集合collection1

./mongoexport -h 192.168.1.12:27017 -uroot -prootroot --authenticationDatabase admin -d db01 -c collection1 -o /data/mongodb/backup1/collection1.json           

導出的資料

{"_id":{"$oid":"5cf8c674bd10185cb185067f"},"name":"hushi","num":0.0}
...           

導出為csv格式

./mongoexport -h 192.168.1.12:27017 -uroot -prootroot --authenticationDatabase admin -d db01 -c collection1 --type=csv -f _id,name,num  -o /data/mongodb/backup1/collection1.csv           
_id,name,num
ObjectId(5cf8c674bd10185cb185067f),hushi,0
ObjectId(5cf8c674bd10185cb185068b),hushi,12
...           

3.2 mongoimport

--headerline: 指明第一行是列名,不需要導入
-csv:指明要導入的是csv格式
-f:指明需要導入的字段           

恢複之前備份的集合

mongoimport -h192.168.1.14:27018 -d test -c collection1 /data/collection1.json           
mongoimport -h 127.0.0.1:27018  -d test1 -c collection1 --type=csv --headerline --file /data/collection1.csv           

4、Percona Server for MongoDB

4.1 熱備原理

可以想象成xtrabackup工具

備份:
首先會啟動一個背景檢測的程序,實時檢測MongoDB Oplog的變化,一旦發現oplog有新的日志寫入,立刻将日志寫入到日志檔案WiredTiger.backup中(你可以strings WiredTiger.backup檢視oplog記錄檔的變化);           

複制MongoDB dbpath的資料檔案和索引檔案到指定的備份目錄裡;

恢複:

将WiredTiger.backup日志進行回放,将記錄檔變更應用到WiredTiger引擎裡,最終得到一緻性快照恢複;把備份目錄裡的資料檔案直接拷貝到你的dbpath下,然後啟動MongoDB即可,會自動接入副本集叢集。
           

4.2 下載下傳安裝

4.2.1 下載下傳

熱備由percon的mongodb版本實作,是以可以直接用percona版的mongodb,也可以将percona mongo當做一個副本集的二級節點。

https://www.percona.com/downloads/percona-server-mongodb-3.6/LATEST/

4.2.2 安裝啟動

因為是要加入副本集,是以修改配置檔案并啟動:

cp mongodb27017/mg27017.conf mongodb27018/mg27018.conf

cp ../mongodb27017/mongo.keyfile .
./mongod -f /data1/mongodb27018/mg27018.conf           

4.2.3 加入副本集

percona 從節點:

在主節點上執行:

replSet27017:PRIMARY> rs.add({host: "192.168.1.12:27018", priority: 1, hidden: false})           

percona 從變為:

>
replSet27017:OTHER>           

執行slaveOk(),為進行過驗證

replSet27017:OTHER> rs.slaveOk()
replSet27017:SECONDARY>           

4.3 備份

從節點進行備份:

replSet27017:SECONDARY> use admin
switched to db admin
replSet27017:SECONDARY> db.runCommand({createBackup: 1, backupDir: "/backup/mongodb_20180704"})
{
    "ok" : 1,
...
開始備份後,立即開始往一個新集合裡寫資料:
replSet27017:PRIMARY> for (i = 0; i < 10000; i++){ db.test6.save({'name':'hushi','num':i,date:new Date()}); }
           

備份完成後的資料目錄:

[root@vm4 /backup/mongodb_20180722]#ll -ht

total 240K

drwx------ 2 root root  221 Jul 22 14:43 test1
drwx------ 2 root root 4.0K Jul 22 14:43 test01
drwx------ 2 root root 4.0K Jul 22 14:43 test
drwx------ 2 root root 4.0K Jul 22 14:43 mms_test
drwx------ 2 root root 4.0K Jul 22 14:43 local
drwx------ 2 root root   38 Jul 22 14:43 journal
drwx------ 2 root root  301 Jul 22 14:43 db01
drwx------ 2 root root  216 Jul 22 14:43 config
drwx------ 2 root root 4.0K Jul 22 14:43 admin
-rw------- 1 root root  44K Jul 22 14:43 sizeStorer.wt
-rw------- 1 root root  114 Jul 22 14:43 storage.bson
-rw------- 1 root root  44K Jul 22 14:43 _mdb_catalog.wt
-rw------- 1 root root 123K Jul 22 14:43 WiredTiger.backup
-rw------- 1 root root   45 Jul 22 14:43 WiredTiger           

4.4 恢複

恢複都單節點,是以去掉配置檔案裡面的副本集資訊

傳輸

rsync -r /backup/mongodb_20180704 192.168.1.13:/backup/

将檔案複制到配置檔案目錄下

cp -R /backup/mongodb_20180704/* /data1/mongodb27018

将keyFile和配置檔案也複制過來(也可以直接去掉驗證,将security相關的參數都注釋)

修改配置檔案後,啟動

./mongod -f /data1/mongodb27018/mg27018.conf

發現最後一條資料的日期晚于開始備份日期,是以驗證了恢複後會直接進行日志重放

5、MongoDB Cloud Manager

5.1 介紹

MongoDB Cloud Manager是官方推出的運維自動化管理系統,是企業版才支援的功能,社群使用者也可以下載下傳試用。

Cloud Manager 主要功能包括

  • MongoDB 叢集(複制集、分片)的自動化部署
  • 叢集監控、及報警定制
  • 自動資料備份與還原

5.2 部署MongoDB Cloud Manager

5.2.1 下載下傳agent和安裝

curl -OL https://cloud.mongodb.com/download/agent/automation/mongodb-mms-automation-agent-10.2.0.5851-1.rhel7_x86_64.tar.gz
tar -xvf mongodb-mms-automation-agent-10.2.0.5851-1.rhel7_x86_64.tar.gz

cd mongodb-mms-automation-agent-10.2.0.5851-1.rhel7_x86_64           

5.2.2 配置

vi local.config
    mmsGroupId=5d2f52fc9ccf64f3c73b2188
    mmsApiKey=          #生成ApiKey
mkdir /var/lib/mongodb-mms-automation   #建立目錄,按照配置檔案裡的路徑
mkdir /var/log/mongodb-mms-automation
chown `whoami` /var/lib/mongodb-mms-automation      #修改歸屬
chown `whoami` /var/log/mongodb-mms-automation
           

5.2.3 啟動agent

nohup ./mongodb-mms-automation-agent --config=local.config >> /var/log/mongodb-mms-automation/automation-agent-fatal.log 2>&1 &           

5.3 進行備份

5.3.1 備份原理

MongoDB學習:備份恢複(一)

部署了agent後,會将整個副本集的資料同步到MongoDB的資料中心。初始同步完成後,agent會将加密和壓縮的oplog資料流式傳輸到Cloud Manager,以提供資料的連續備份。

5.3.2 開始備份

點選start即可開始備份

MongoDB學習:備份恢複(一)

可以看到oplog一直是延遲極小的

MongoDB學習:備份恢複(一)

mongodb的備份,預設6小時一次。然後oplog一直備份,秒級延遲。

[root@vm4 /backup/replSet27017]#ll -ht

total 288K

drwxr-xr-x 2 root root  176 Jul 18 16:04 test1
drwxr-xr-x 2 root root 4.0K Jul 18 16:04 test01
drwxr-xr-x 2 root root 4.0K Jul 18 16:04 test
drwxr-xr-x 2 root root  172 Jul 18 16:04 mms_test
drwxr-xr-x 2 root root  299 Jul 18 16:04 db01
drwxr-xr-x 2 root root 4.0K Jul 18 16:04 admin
-rw-r--r-- 1 root root  114 Jul 18 16:04 storage.bson
-rw-r--r-- 1 root root  36K Jul 18 16:04 sizeStorer.wt
-rw-r--r-- 1 root root  40K Jul 18 16:04 _mdb_catalog.wt
-rw-r--r-- 1 root root  373 Jul 18 16:04 restoreInfo.txt
-rw-r--r-- 1 root root  755 Jul 18 16:04 seedSecondary.bat
-r-x---r-x 1 root root  768 Jul 18 16:04 seedSecondary.sh
-rw-r--r-- 1 root root 4.0K Jul 18 16:04 WiredTigerLAS.wt
-rw-r--r-- 1 root root 168K Jul 18 16:04 WiredTiger.wt
-rw-r--r-- 1 root root   45 Jul 18 16:04 WiredTiger
-rw-r--r-- 1 root root   21 Jul 18 16:04 WiredTiger.lock
-rw-r--r-- 1 root root 1.2K Jul 18 16:04 WiredTiger.turtle           

下載下傳到本地恢複,可以發現oplog應用點和快照點時間基本是一緻的:

[root@vm4 /backup/replSet27017]#cat restoreInfo.txt 
Restore Information
Group Name: testProject1
Replica Set: replSet27017
Snapshot timestamp: Thu Jul 18 04:12:24 GMT 2019
Last Oplog Applied: Thu Jul 18 04:12:23 GMT 2019 (1563423143, 1)           

可以發現沒有local庫和journal庫,是以不能直接恢複加入到副本集裡,更适合資料誤删恢複。

5.4 進行恢複

5.4.1 恢複原理

備份後的檔案,是實體檔案,下載下傳後的資料是備份結束時刻的資料(應用了備份期間oplog)

因為mongodb的備份,預設6小時一次。然後oplog一直備份,秒級延遲。是以恢複的時候有了任何時刻的oplog了。

恢複有三種選項:

  • 恢複快照(也就是恢複到備份結束時刻)
  • 恢複到oplog timestamp
  • 恢複到point in time(也是轉換成oplog timestamp)

    5.4.2 開始恢複

MongoDB學習:備份恢複(一)

線上恢複隻能恢複到一個新的副本集

隻下載下傳備份,就是備份結束時刻;選擇oplog點,就是繼續應用oplog
按照上圖所示選擇時間點,會生成一條對應的指令:
           

./mongodb-backup-restore-util --host --port  --rsId replSet27017 --groupId 5d2d88adf2a30bbee892ef0d --opStart 1563423143:1 --opEnd 1563430620:502 --oplogSourceAddr

https://api-backup.us-east-1.mongodb.com

--apiKey

是直接調用了mongodb官方的API,應用oplog。

mongodb-backup-restore-util 是官方提供的恢複工具。

選擇point time:

./mongodb-backup-restore-util --host --port --rsId replSet27017 --groupId 5d2d88adf2a30bbee892ef0d --opStart 1563423143:1 --opEnd 1563429600:0 --oplogSourceAddr

發現生成的指令和選擇oplog timestamp是一樣的,隻是把point time轉換成oplog timestamp。

seedSecondary.sh檔案:

因為mongodb cloud manager提供的全備恢複後沒有local庫,是以會自動生成一個建立oplog的檔案。用于建立oplog,插入一條資料,指定副本集複制的點。

[root@vm4 /backup/replSet27017]#cat seedSecondary.sh 
#!/bin/sh
if [ "$#" -ne 4 ]; then
    echo "Usage: $0 MONGODB_PORT OPLOG_SIZE_GB RS_NAME PRIMARY_HOST:PRIMARY_PORT"
    exit 1
fi
mongo --port ${1} --eval "var res=db.getSiblingDB('local').runCommand({ create: 'oplog.rs', capped: true, size: (${2} * 1024 * 1024 * 1024)}); if(!res.ok){throw res.errmsg;} else{db.getSiblingDB('local').oplog.rs.insert({ts : Timestamp((db.version().match(/^2\.[012]/) ? 1000 : 1) * 1563423143, 1), h : NumberLong('8796150802864664169'), t : NumberLong('113'), op : 'n', ns : '', o : { msg : 'seed from backup service' }});if (db.getSiblingDB('local').system.replset.count({'_id': '${3}'}) == 0) { db.getSiblingDB('local').system.replset.insert({'_id' : '${3}','version' : 1,'members' : [{'_id' : 0,'host' :'${4}'}],'settings' : {}});}}"           

二、備份恢複演練

1、還原到副本集

MongoDB運作了一段時間之後,為了新加一個節點,就需要先将備份資料恢複到一個單節點,再将單節點加入到副本集裡,下面将示範使用percona server備份的資料進行還原。

1.1 得到全部實體備份

replSet27017:SECONDARY> db.runCommand({createBackup: 1, backupDir: "/backup/mongodb_20180722"})
開始備份後立即開始往新集合裡寫資料:
replSet27017:PRIMARY> for (i = 0; i < 10000; i++){ db.test5.save({'name':'hushi','num':i,date:new Date()}); }
WriteResult({ "nInserted" : 1 })           

将備份傳輸到新的實體機:

rsync -r /backup/mongodb_20180722 192.168.1.14:/backup/

1.2 進行恢複

為了加入到副本集,是以啟動的時候直接用配置檔案啟動,将mg27017.conf和mongo.keyfile複制到本地,再将資料檔案複制到配置檔案設定的目錄下。

配置檔案進行相應的修改,先将副本集相關的參數去掉

啟動

./mongod -f /data1/mongodb27017/mg27017.conf

檢視恢複之後最後一條非空的oplog

> db.oplog.rs.find({op:{$ne:'n'}}).sort({$natural:-1}).limit(1).pretty()
{
    "ts" : Timestamp(1563777601, 5),
    "t" : NumberLong(123),
    "h" : NumberLong("-6583634829168454121"),
    "v" : 2,
    "op" : "i",
    "ns" : "config.system.sessions",
    "ui" : UUID("10b4d6ac-37df-4586-8a2d-83a3389d7406"),
    "wall" : ISODate("2019-07-22T06:40:01.172Z"),
    "o" : {
        "_id" : {
            "id" : UUID("9805a821-9a88-43bf-a85d-9a0b63b98632"),
            "uid" : BinData(0,"Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=")
        },
        "lastUse" : ISODate("2019-07-22T06:40:01.175Z"),
        "user" : {
            "name" : "root@admin"
        }
    }
}           

發現是系統庫的,是以直接查找對test5集合的操作:

> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test5'}).sort({$natural:-1}).limit(1).pretty()
{
    "ts" : Timestamp(1563777479, 674),
    "t" : NumberLong(123),
    "h" : NumberLong("6274301431493104273"),
    "v" : 2,
    "op" : "i",
    "ns" : "mms_test.test5",
    "ui" : UUID("3fd00235-24ea-447a-96c5-2d79071ad9df"),
    "wall" : ISODate("2019-07-22T06:37:59.619Z"),
    "o" : {
        "_id" : ObjectId("5d3559c7dde424d0301c3d21"),
        "name" : "hushi",
        "num" : 41015,
        "date" : ISODate("2019-07-22T06:37:59.623Z")
    }
}           

1.3 增備備份

因為oplog的特性,多跑一些沒有關系,是以上述是直接找到了對test5集合操作的oplog點,現在去找到最新的點。主要要保證上述點和最新的點之間的oplog必須存在。

先副本集裡找到離現在較近的點:

replSet27017:SECONDARY> db.oplog.rs.find({op:{$ne:'n'}}).sort({$natural:-1}).limit(1).pretty()

{
    "ts" : Timestamp(1563780036, 337),
    "t" : NumberLong(123),
    "h" : NumberLong("-2687468098839248386"),
    "v" : 2,
    "op" : "i",
    "ns" : "admin.test10",
    "ui" : UUID("083637d7-6209-4d28-9b4d-529ba26e836c"),
    "wall" : ISODate("2019-07-22T07:20:36.370Z"),
    "o" : {
        "_id" : ObjectId("5d3563c4fc6d8027c7545934"),
        "name" : "hushi",
        "num" : 41881,
        "date" : ISODate("2019-07-22T07:20:36.373Z")
    }
           

按照上述的oplog點進行dump:

./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin  -d local -c oplog.rs  -o /backup --query='{ts:{$gte:Timestamp(1563777479, 674),$lt:Timestamp(1563780036, 337)}}'
2019-07-22T15:42:40.338+0800    done dumping local.oplog.rs (52140 documents)
2019-07-22T15:42:40.342+0800    dump phase III: the oplog
2019-07-22T15:42:40.342+0800    finishing dump           

1.4 将增量進行恢複

mv /backup/local/oplog.rs.bson

rsync /backup/local/oplog.bson 192.168.1.14:/backup/local/

恢複的時候報錯:

Failed: restore error: error applying oplog: applyOps: not authorized on admin to execute command { applyOps: [ { ts: Timestamp(1563777601, 1), h: 2702590434054582905, v: 2, op: "d", ns: "config.system.sessions", o: { _id: { id: UUID("9f7a5b0b-a1e1-407a-b9dd-7506a3220d9e"), uid: BinData(0, 6399AB0DAC62F20BFC466753B10FB58FB7E692BEC952C69B84D997021794D1F8) } }, o2: {} } ], $db: "admin" }           

解決方法(在admin資料庫中執行):

db.createRole({role:'sysadmin',roles:[], privileges:[ {resource:{anyResource:true},actions:['anyAction']}]})
db.grantRolesToUser( "root" , [ { role: "sysadmin", db: "admin" } ])            

進行恢複

#./mongorestore -vv -h127.0.0.1:27017 -uroot -prootroot --authenticationDatabase admin --oplogReplay /backup/local/
2019-07-22T16:11:03.901+0800    oplog  7.71MB
2019-07-22T16:11:06.401+0800    applied 51899 ops
2019-07-22T16:11:06.401+0800    oplog  9.13MB
2019-07-22T16:11:06.401+0800    done           

資料已經進行了恢複:

switched to db admin
> db.test10.find().sort({$natural:-1}).limit(1).pretty()
{
    "_id" : ObjectId("5d3563c4fc6d8027c7545933"),
    "name" : "hushi",
    "num" : 41880,
    "date" : ISODate("2019-07-22T07:20:36.373Z")
}           

如果将單節點以一個新的單節點副本集去啟動,那就需要删除掉所有的oplog,再重新啟動。那就是跟直接将節點加入副本集是一樣的。

> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'}}).sort({$natural:-1}).limit(1).pretty()
{
    "ts" : Timestamp(1563777601, 5),
    "t" : NumberLong(123),
    "h" : NumberLong("-6583634829168454121"),
    "v" : 2,
    "op" : "i",
    "ns" : "config.system.sessions",
    "ui" : UUID("10b4d6ac-37df-4586-8a2d-83a3389d7406"),
    "wall" : ISODate("2019-07-22T06:40:01.172Z"),
    "o" : {
        "_id" : {
            "id" : UUID("9805a821-9a88-43bf-a85d-9a0b63b98632"),
            "uid" : BinData(0,"Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=")
        },
        "lastUse" : ISODate("2019-07-22T06:40:01.175Z"),
        "user" : {
            "name" : "root@admin"
        }
    }
}           

1.5 加入到副本集

replSet27017:PRIMARY> rs.add({host: "192.168.1.14:27017", priority: 1, hidden: false})
{
    "ok" : 1,           

加入之後,oplog記錄也一樣了:

replSet27017:SECONDARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ts:{$lte:Timestamp(1563780036, 337)}}).sort({$natural:-1}).limit(1).pretty()
{
    "ts" : Timestamp(1563780036, 337),
    "t" : NumberLong(123),
    "h" : NumberLong("-2687468098839248386"),
    "v" : 2,
    "op" : "i",
    "ns" : "admin.test10",
    "ui" : UUID("083637d7-6209-4d28-9b4d-529ba26e836c"),
    "wall" : ISODate("2019-07-22T07:20:36.370Z"),
    "o" : {
        "_id" : ObjectId("5d3563c4fc6d8027c7545934"),
        "name" : "hushi",
        "num" : 41881,
        "date" : ISODate("2019-07-22T07:20:36.373Z")
    }
}           

2、恢複誤删的單個集合

插入資料

for (i = 0; i < 10000; i++){ db.collection1.save({'name':'hushi','num':i}); }

collection1集合備份

./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d db01 -c collection1  -o /data/mongodb/backup

再次插入資料(進行一些操作)

for (i = 0; i < 1000; i++){ db.collection1.save({'name':'hushi','num':i,date:new Date()}); }

誤删

db.collection1.drop()

查找删除

db.oplog.rs.find({op:{$ne:'n'},o:{'drop':'collection1'}}).sort({ts:-1}).pretty().limit(5)
{
    "ts" : Timestamp(1559803854, 1),
    "t" : NumberLong(44),
    "h" : NumberLong("-514875429754097077"),
    "v" : 2,
    "op" : "c",
    "ns" : "db01.$cmd",
    "ui" : UUID("b8fe3553-8249-4f18-b652-5bba86760d43"),
    "wall" : ISODate("2019-06-06T06:50:54.406Z"),
    "o" : {
        "drop" : "collection1"
    }
}           

備份oplog

./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d local -c oplog.rs -o /tmp           

restore的時候需要是oplog.bson

mv /tmp/local/oplog.rs.bson /data/mongodb/backup/oplog.bson

恢複collection1

./mongorestore -v -h127.0.0.1:27018  -d db01 -c collection1 /data/backup1/db01/collection1.bson

檢視已經恢複前1w插入記錄

db.collection1.count()

10000

應用oplog

mongorestore -vv  -h127.0.0.1:27018  --oplogReplay  --oplogLimit=1559803854:1  /data/backup1/

再次檢視插入記錄

11000

3、恢複誤删的文檔

文檔誤删除

replSet27017:PRIMARY> for (i = 0; i < 1000; i++){ db.test4.save({'name':'hushi','num':i,date:new Date()}); }

replSet27017:PRIMARY> db.test4.remove({$and:[{num:{$gte:100,$lte:300}}]})
WriteResult({ "nRemoved" : 201 })

replSet27017:PRIMARY> for (i = 1000; i < 1500; i++){ db.test4.save({'name':'hushi','num':i,date:new Date()}); }
WriteResult({ "nInserted" : 1 })
           

找到删除開始時間點

replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4'},{ts:1,op:1}).sort({$natural:-1}).limit(10).skip(695)
{ "ts" : Timestamp(1563431039, 6), "op" : "d" }
{ "ts" : Timestamp(1563431039, 5), "op" : "d" }
{ "ts" : Timestamp(1563431039, 4), "op" : "d" }
{ "ts" : Timestamp(1563431039, 3), "op" : "d" }
{ "ts" : Timestamp(1563431039, 2), "op" : "d" }
{ "ts" : Timestamp(1563431039, 1), "op" : "d" }
{ "ts" : Timestamp(1563430620, 502), "op" : "i" }
{ "ts" : Timestamp(1563430620, 501), "op" : "i" }
{ "ts" : Timestamp(1563430620, 500), "op" : "i" }
{ "ts" : Timestamp(1563430620, 499), "op" : "i" }
           

大于等于這個時間點,删除了201條資料

replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4',op:'d',ts:{$gte:Timestamp(1563431039, 1)}}).sort({$natural:-1}).limit(1).count()
201           
replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4',op:'d',ts:{$gte:Timestamp(1563431039, 1)}}).sort({$natural:-1}).limit(1).pretty()
{
    "ts" : Timestamp(1563431039, 201),
    "t" : NumberLong(113),
    "h" : NumberLong("-1345379834581505732"),
    "v" : 2,
    "op" : "d",
    "ns" : "mms_test.test4",
    "ui" : UUID("9a3dbae7-4f7c-465d-b420-1b7818531fc8"),
    "wall" : ISODate("2019-07-18T06:23:59.997Z"),
    "o" : {
        "_id" : ObjectId("5d300edb42389abced5f7bae")
    }
}
replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4',op:'d',ts:{$gte:Timestamp(1563431039, 1)}}).sort({$natural:1}).limit(1).pretty()
{
    "ts" : Timestamp(1563431039, 1),
    "t" : NumberLong(113),
    "h" : NumberLong("-4777761962883174315"),
    "v" : 2,
    "op" : "d",
    "ns" : "mms_test.test4",
    "ui" : UUID("9a3dbae7-4f7c-465d-b420-1b7818531fc8"),
    "wall" : ISODate("2019-07-18T06:23:59.997Z"),
    "o" : {
        "_id" : ObjectId("5d300edb42389abced5f7ae6")
    }
}
           

這裡最後試用了一下ongodb-backup-restore-util,如果使用mongorestore是一樣的,但是需要先将oplog按照時間點進行dump下來,再去進行恢複。

#./mongodb-backup-restore-util --host 127.0.0.1 --port 27017  --rsId replSet27017 --groupId 5d2d88adf2a30bbee892ef0d --opStart 1563423143:1 --opEnd 1563430620:502   --oplogSourceAddr https://api-backup.us-east-1.mongodb.com --apiKey 5d2f0fbbff7a252987d03c00086f7a1040706ba4c6c634b8f4c09b9e
[2019/07/18 19:13:55.843] [pit-restore.debug] [pit-restore/standalone-pit-restore.go:main:116] Creating restore
[2019/07/18 19:14:05.406] [pit-restore.debug] [pit-restore/standalone-pit-restore.go:main:137] Successfully completed restore.
[2019/07/18 19:14:05.406] [pit-restore.debug] [pit-restore/standalone-pit-restore.go:main:138] Successfully completed restore.
[2019/07/18 19:14:05.406] [pit-restore.info] [restore/restore.go:Stop:121] Stopping Restore
           

已經進行了恢複

db.test4.find({$and:[{num:{$gte:100,$lte:300}}]}).count()

201

三、備份恢複方案

1、備份政策

1.1 MongoDB Cloud Manager的備份政策:

The rate is based on your having 28 snapshots at steady state:

The six-hour snapshots are kept for two days;
The daily snapshots for one week,
The weekly snapshots for one month,
The monthly for one year.           

這是官方提供的全備和全備保留的時間點。

oplog是一直備份,秒級延遲。

1.2 全備 + oplog

順着這個思路,全備可以和增備分開,全備照需要,每6個小時或者每一天備份一次。然後oplog每秒或者每5秒或者每1分鐘進行一次備份。

全備可以使用percona server進行實體熱備,oplog可以按照設定的時間間隔一直備份。

再可以按将一些重要的文檔使用mysqldump進行備份,這樣萬一有誤操作可以快速恢複。

2、增備oplog

2.1 進行增備

#./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin  -d local -c oplog.rs  -o /backup --query='{ts:{$gte:Timestamp(1563703200, 1),$lt:Timestamp(1563703500, 1)}}'           

這裡是增備了5分鐘的oplog内容

将增備oplog改名:

mv oplog.rs.bson oplog.bson.backup

./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin  -d local -c oplog.rs  -o /backup --query='{ts:{$gte:Timestamp(1563771600, 1),$lt:Timestamp(1563775200, 1)}}'           

再次增備,将增備的oplog合到一個bson檔案裡

cat oplog.rs.bson >>oplog.bson.backup

接下來繼續增備,繼續将增備都追加到一個檔案裡。

可以将每一天的增備放到一個檔案裡。

3、驗證備份

進行了備份之後肯定要進行相關的驗證,驗證分為兩塊,一個是備份檔案恢複後完整性驗證,一個是oplog增備是否成功驗證。

一個思路是每天将全備的檔案和增備的檔案恢複到一個單節點。

3.1 驗證全備

全備建議使用實體熱備,如果使用的是percona server,那麼可以WiredTiger.backup檔案的最後一條插入記錄在恢複的節點上是否存在;如果沒有插入記錄,或者WiredTiger.backup檔案沒有内容,可以簡單比較一下副本集的集合資料和恢複後的節點是否相同。同樣如果使用的是mongodb cloud manager這樣的不記錄oplog的工具,也是比較一下集合數量。

3.2 驗證增備

驗證增備可以檢視bson檔案裡最後一條插入的資料是什麼,檢視恢複後的檔案是否有這條資料。