MongoDB案例分享:如何使用oplog恢復資料
最近跟資料恢復槓上了,這不又來一例。關於備份恢復的問題其實我在6年多以前就寫過,其中大部分討論放在今天仍然適用。
1
案例介紹
某使用者使用了MongoDB 4.0,資料庫中的一個表因為
drop
操作導致資料全部丟失。但因為庫本身很小,而
oplog
空間足夠大,所以從建庫至今的所有操作都尚在
oplog
中沒有被回收。基於這種情況,雖然他們沒有全量備份,我們仍然可以通過完整重放
oplog
來找回所有丟失的資料。所以我們的操作是:
-
匯出
oplog
; -
尋找
drop
發生的時間戳; -
重放到
drop
前一刻; -
將恢復的資料dump/restore到生產庫;
步驟4屬於基本操作就不詳細敘述了,主要來看前面3步。
2
恢復步驟
2.1 匯出oplog
這一步實際上特別簡單。
oplog
位於
local.oplog.rs
集合中,我們可以使用
mongodump
直接匯出,匯出節點可以是主節點或從節點。基本形式是:
mongodump --host <host>:<port> -d local -c oplog.rs -u <user> --authenticationDatabase <adb>
得到如下輸出:
> tree dump
dump
└──
local
├── oplog.rs.bson
└── oplog.rs.metadata.json
1 directory, 2 files
我們需要的就是
oplog.rs.bson
。
2.2 尋找截止時間戳
進行重放的關鍵是要先找出重放截止到哪條
oplog
。這裡有兩種辦法:
方案1
從
oplog.rs.bson
中搜尋關鍵字
drop
。
> bsondump dump/local/oplog.rs.bson
| grep drop
{
"ts
":{
"
$timestamp
":{
"t
":1646056645,
"i
":1}},
"t
":{
"
$numberLong
":
"1
"},
"h
":{
"
$numberLong
":
"7307295890643732087
"},
"v
":{
"
$numberInt
":
"2
"},
"op
":
"c
",
"ns
":
"test.
$cmd
",
"ui
":{
"
$binary
":{
"base64
":
"9sakiEOMS2qjwBZ5O0mQjQ==
",
"subType
":
"04
"}},
"wall
":{
"
$date
":{
"
$numberLong
":
"1646056645661
"}},
"o
":{
"drop
":
"survey
"}}
如果多次出現
drop
記錄,則要自己注意辨別哪條是你想要的那條。然後注意記錄中
{"t":1646056645,"i":1}
是我們要截止到的時間戳,後面將會用到這個資料。
另外注意如果
oplog
較多時該辦法可能會耗時較長。
方案2
從
local.oplog.rs
中查詢。這種查詢方法通常會比方案1快,但需要在原始系統上執行查詢,可能造成一定的負擔。如果系統本身壓力已經較大,則要注意避開業務高峰期。另外也可以在從節點上執行查詢以避開壓力最大的主節點。這裡要注意的是每個節點上儲存的
oplog
可能不一樣多,但一定是一致的。例如,某個節點上的
oplog
有1,2,3,4,5共計5條,其他節點上可能只有:
-
2,3,4,5
-
3,4,5
-
4,5
-
5
這種情況通常是由於從節點是後來加進叢集裡導致的。那麼想要查詢時,可以使用:
>
use
local
>
db.
oplog
.
rs
.
find
({
"o.drop":
{
$exists:
true
}}).
sort
({
$natural:
-1
}).
limit
(
1
);
{
"ts" :
Timestamp
(
1646056729
,
1
),
"t" :
NumberLong
(
1
),
"h" :
NumberLong
(
"6882491835596436855"
),
"v" :
2
,
"op" :
"c"
,
"ns" :
"test.$cmd"
,
"ui" :
UUID
(
"a98cba5a-066b-46fe-92a9-d122386dba5d"
),
"wall" :
ISODate
(
"2022-02-28T13:58:49.167Z"
),
"o" :
{
"drop" :
"survey"
}
}
同樣注意
Timestamp(1646056729, 1)
是我們將要用到的截止時間戳。
2.3 重放oplog
mongorestore
本身是用來恢復
bson
檔案的同時順便重放
oplog
的。現在我們沒有
bson
要恢復,只有
oplog
要重放,所以需要點小花招來欺騙
mongorestore
,那就是用一個空資料夾:
mkdir empty
mongorestore --host
<host
>:
<port
> -u
<user
> --authenticationDatabase
<adb
> \
--oplogReplay \
--oplogFile dump/local/oplog.rs.bson \
--oplogLimit 1646056729:1 \
empty/
注意:這裡應該在一個新的例項上完成重放操作。
重放完成後,你就擁有了一份截止到
drop
操作前的完整資料。
3
改進方案
上面的步驟雖然可以完成任務,但有些浪費。因為丟失的只有一個表,我們卻恢復了整個資料庫,消耗了不必要的時間。有沒有辦法只恢復丟失的那一個表呢?從原理來講是可以辦到的,那就是隻重放這個表上的
oplog
,那麼只需要在匯出
oplog
的時候做個過濾就可以辦到了:
mongodump --host <host>:<port> -d local -c oplog.rs -u <user> --authenticationDatabase <adb> -q '{"ns": "test.survey"}'
後續步驟沒有什麼差異,就不再贅述了。但是這樣的做法有個bug,那就是
事務。我一開始也栽在了這個問題上。事務會把多條操作放在一條
oplog
裡,以此來保證事務的原子性。比如如下事務操作:
var
mongo
=
db.
getMongo
();
var
session
=
mongo.
startSession
();
session.
startTransaction
();
var
coll
=
session.
getDatabase
(
"test"
).
getCollection
(
"survey"
);
coll.
insertOne
({
y:
1
});
coll.
insertOne
({
y:
2
});
coll.
insertOne
({
y:
3
});
session.
commitTransaction
();
其產生的
oplog
是這樣的:
{
"ts":
Timestamp
(
1646057834
,
1
),
"t":
NumberLong
(
1
),
"h":
NumberLong
(
"-2362908976881142089"
),
"v":
2
,
"op":
"c"
,
"ns":
"admin.$cmd"
,
"wall":
ISODate
(
"2022-02-28T14:17:14.189Z"
),
"lsid":
{
"id":
UUID
(
"02ca1f7e-f451-4ec3-946f-cf307c0d03b7"
),
"uid":
BinData
(
0
,
"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
)
},
"txnNumber":
NumberLong
(
1
),
"stmtId":
0
,
"prevOpTime":
{
"ts":
Timestamp
(
0
,
0
),
"t":
NumberLong
(
-1
)
},
"o":
{
"applyOps":
[{
"op":
"i"
,
"ns":
"test.survey"
,
"ui":
UUID
(
"04a8b634-4048-48a6-b358-9a879c1a20ed"
),
"o":
{
"_id":
ObjectId
(
"621cd969a3a94c2e74b595c5"
),
"y":
1
}
},
{
"op":
"i"
,
"ns":
"test.survey"
,
"ui":
UUID
(
"04a8b634-4048-48a6-b358-9a879c1a20ed"
),
"o":
{
"_id":
ObjectId
(
"621cd969a3a94c2e74b595c6"
),
"y":
2
}
},
{
"op":
"i"
,
"ns":
"test.survey"
,
"ui":
UUID
(
"04a8b634-4048-48a6-b358-9a879c1a20ed"
),
"o":
{
"_id":
ObjectId
(
"621cd969a3a94c2e74b595c7"
),
"y":
3
}
}]
}
}
可見這裡的
{"ns": "admin.$cmd"}
並不在
test.survey
上,所以上面的過濾辦法會把事務產生的資料都排除在外,就會造成一部分資料丟失。解決辦法也很簡單,修改一下過濾條件:
mongodump --host <host>:<port> -d local -c oplog.rs -u <user> --authenticationDatabase <adb> -q '{"$or": [{"ns": "test.survey"}, {"o.applyOps.ns": "test.survey"}]}'
4
結束語
這個案例是個很極端的情況,所以不要想著抄作業,你幾乎一定不會遇到相同的場景。但恢復的原理卻是相通的,無論何種備份恢復都是“全量”+“增量”的做法,只要你理解了原理,剩下的就是動手嘗試而已。
關於作者: 張耀星
MongoDB中文社群常委會委員,論壇聯席主席。
MongoDB公司北亞區首席技術諮詢服務顧問。在MongoDB的開發、應用和諮詢服務方面,擁有多年的豐富實踐經驗。
作為MongoDB認證專家,曾經為不同行業的各類大型客戶提供過培訓、效能調優、架構設計等各類技術及諮詢服務,頗得廣大客戶信任。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69961190/viewspace-2869415/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【北亞資料恢復】MongoDB資料遷移檔案丟失的MongoDB資料恢復案例資料恢復MongoDB
- mongodb使用備份後的oplog做時間點恢復MongoDB
- 【資料庫資料恢復】MongoDB資料庫檔案損壞的資料恢復案例資料庫資料恢復MongoDB
- mongodb 基於oplog的時間點恢復MongoDB
- 【資料庫資料恢復】無法啟動MongoDB服務的資料恢復案例資料庫資料恢復MongoDB
- 使用WT工具恢復MongoDB資料MongoDB
- 【北亞資料恢復】sqlserver資料庫被加密的資料恢復案例分享資料恢復SQLServer資料庫加密
- 資料庫資料恢復——Windows無法啟動MongoDB服務的資料恢復案例資料庫資料恢復WindowsMongoDB
- NoSQL 資料庫案例實戰 -- MongoDB資料備份、恢復SQL資料庫MongoDB
- 【資料庫資料恢復】MongoDB資料庫服務啟動失敗的資料恢復案例資料庫資料恢復MongoDB
- 【資料庫資料恢復】mdb_catalog.wt檔案丟失的MongoDB資料恢復案例資料庫資料恢復MongoDB
- 資料庫資料恢復—MongoDB資料庫檔案丟失,啟動報錯的資料恢復案例資料庫資料恢復MongoDB
- AUL恢復資料案例
- 【資料庫資料恢復】SAP資料庫資料恢復案例資料庫資料恢復
- MongoDB資料庫報錯,資料庫檔案丟失資料恢復案例MongoDB資料庫資料恢復
- 【Vsan資料恢復】Vsan資料恢復案例資料恢復
- Vsan資料恢復—Vsan資料恢復案例資料恢復
- 【資料庫資料恢復】Sql Server資料庫資料恢復案例資料庫資料恢復SQLServer
- 伺服器資料恢復-ESX SERVER資料恢復案例伺服器資料恢復Server
- 【伺服器資料恢復】Vsan資料恢復案例伺服器資料恢復
- 【伺服器資料恢復】SUN SOLARIS資料恢復案例伺服器資料恢復
- 資料庫恢復案例大全資料庫
- 北亞企安資料恢復-XSAN資料恢復案例資料恢復
- 【VSAN資料恢復】VSAN儲存資料恢復案例資料恢復
- 儲存崩潰資料恢復過程;資料恢復案例資料恢復
- EMC 儲存資料恢復案例詳解【資料恢復方案】資料恢復
- 使用netbackup進行資料庫恢復案例資料庫
- MySQL 資料庫增量資料恢復案例MySql資料庫資料恢復
- 【虛擬機器資料恢復】VMware ESX SERVER資料恢復案例虛擬機資料恢復Server
- 伺服器資料恢復-VMwave虛擬化資料恢復案例伺服器資料恢復
- 【伺服器資料恢復】HP EVA儲存資料恢復案例伺服器資料恢復
- 【伺服器資料恢復】raid5資料恢復案例伺服器資料恢復AI
- 【伺服器資料恢復】伺服器硬碟資料恢復案例伺服器資料恢復硬碟
- 【伺服器資料恢復】raid0資料恢復案例&raid資料回遷案例伺服器資料恢復AI
- 【資料庫資料恢復】Oracle資料庫誤truncate table的資料恢復案例資料庫資料恢復Oracle
- nbu恢復oracle資料庫案例Oracle資料庫
- 練習:資料庫恢復案例資料庫
- 資料庫常規恢復案例資料庫