使用 Wiredtiger 存儲引擎的 MongoDB 無法啟動,數(shù)據(jù)能否恢復(fù)回來?
問題出現(xiàn)的場景是「mongod磁盤寫滿了,導(dǎo)致進(jìn)程 crash」,嘗試重新啟動,結(jié)果 wiredtiger 報(bào)錯,錯誤信息類似如下,說明此時 MongoDB 數(shù)據(jù)文件已經(jīng)損壞。
2017-03-28T22:06:05.315-0500 W -? ? ? ? [initandlisten] Detected unclean shutdown - /data/mongodb/mongod.lock is not empty.
2017-03-28T22:06:05.315-0500 W STORAGE? [initandlisten] Recovering data from the last clean checkpoint.
2017-03-28T22:06:05.324-0500 I STORAGE? [initandlisten] wiredtiger_open config: create,cache_size=13G,session_max=20000,eviction=(threads_max=4),statistics=(fast),log=(enabled=true,archive=true
,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(wait=60,log_size=2GB),statistics_log=(wait=0),
2017-03-28T22:06:05.725-0500 E STORAGE? [initandlisten] WiredTiger (0) [1454119565:724960][1745:0x7f2ac9534bc0], file:WiredTiger.wt, cursor.next: read checksum error for 4096B block at offset 6
799360: block header checksum of 1769173605 doesn't match expected checksum of 4176084783
2017-03-28T22:06:05.725-0500 E STORAGE? [initandlisten] WiredTiger (0) [1454119565:725067][1745:0x7f2ac9534bc0], file:WiredTiger.wt, cursor.next: WiredTiger.wt: encountered an illegal file form
at or internal value
2017-03-28T22:06:05.725-0500 E STORAGE? [initandlisten] WiredTiger (-31804) [1454119565:725088][1745:0x7f2ac9534bc0], file:WiredTiger.wt, cursor.next: the process must exit and restart: WT_PANI
C: WiredTiger library panic
2017-03-28T22:06:05.725-0500 I -? ? ? ? [initandlisten] Fatal Assertion 28558
MongoDB 3.2及以后的版本已經(jīng)很少會出現(xiàn)這樣的問題,至少從我接觸 MongoDB 到現(xiàn)在還沒實(shí)際遇到過這個問題,不過既然問題已經(jīng)發(fā)生,我們來看看遇到這種情況應(yīng)該怎么恢復(fù)數(shù)據(jù)?
如何恢復(fù) MongoDB 數(shù)據(jù)?
第一招: 從復(fù)制集其他節(jié)點(diǎn)同步數(shù)據(jù)
MongoDB 通過復(fù)制集能保證高可靠的數(shù)據(jù)存儲,通常生產(chǎn)環(huán)境建議使用「3節(jié)點(diǎn)復(fù)制集」,這樣即使其中一個節(jié)點(diǎn)崩潰了無法啟動,我們可以直接將其數(shù)據(jù)清掉,重新啟動后,以全新的 Secondary 節(jié)點(diǎn)加入復(fù)制集,它會自動的同步數(shù)據(jù),這樣也就達(dá)到了恢復(fù)數(shù)據(jù)的目的。
然而不幸的是,該用戶的 MongoDB 實(shí)例 只部署了一個節(jié)點(diǎn) ... 我只能呵呵了 ...
第二招:從最近的一個備份集恢復(fù)數(shù)據(jù)
有的時候可能出現(xiàn)一些極端的case,比如遇到自然災(zāi)害,復(fù)制集所有節(jié)點(diǎn)都掛了(或者像上面的用戶這樣,你的復(fù)制集只部署一個節(jié)點(diǎn)...),這時第一招就沒法用了。
此時,如果靠譜的你剛好對數(shù)據(jù)做了備份,此時就排上用場了,比如你每天對 MongoDB 做一次全量備份,那么你就可以把數(shù)據(jù)恢復(fù)到最近一天的數(shù)據(jù)了;如果你更靠譜的還對數(shù)據(jù)做了增量本分,能恢復(fù)的數(shù)據(jù)就更多了。
但是可想而知,這個用戶既然能部署一個「只有單個節(jié)點(diǎn)的復(fù)制集」,肯定也不會想到去對數(shù)據(jù)庫進(jìn)行備份了 ...
第三招: repair 模式啟動 MongoDB
當(dāng) MongoDB 無法啟動時,通常是因?yàn)閿?shù)據(jù)文件出現(xiàn)了不一致,mongod 支持以 repair 的模式啟動,mongod 會盡可能的嘗試自己去修復(fù)數(shù)據(jù)的不一致狀態(tài),修復(fù)過程中盡可能多的保留有效的數(shù)據(jù)。
但 repair 也不是對所有的場景都有效,repair 會先加載 MongoDB 所有的集合信息,然后針對每個集合來 repair,如果存儲元數(shù)據(jù)的數(shù)據(jù)文件損壞,repair 也是沒法工作的。
mongod --repair // 用戶嘗試按這種方式啟動,仍然報(bào)相同的錯誤
第四招:使用 wireditger 工具恢復(fù)
以上3招都不行,我的第一想法就是通過 wiredtiger 的 salvage 功能去盡可能的恢復(fù)數(shù)據(jù)(salvage 可翻譯為數(shù)據(jù)打撈,即針對一個wt的數(shù)據(jù)文件,盡可能多的從中提取有效的數(shù)據(jù)),本來是想寫個工具來做這個事情。不過調(diào)研了一下發(fā)現(xiàn)
1. repair 模式啟動,實(shí)現(xiàn)時也是調(diào)用的 wiredtiger 的 salvage 接口實(shí)現(xiàn)。
2. wireditger 自帶的一個命令行工具 wt,包含了 salvage 的功能。
3. 找到一篇使用 wt 工具恢復(fù) MongoDB 數(shù)據(jù)的文章,寫的非常贊。
網(wǎng)友總結(jié)的使用 wiredtiger 工具 wt 恢復(fù)數(shù)據(jù)的方法原理很簡單,就是通過恢復(fù) wiredtiger 數(shù)據(jù)文件來恢復(fù)MongoDB數(shù)據(jù),我實(shí)驗(yàn)了一下,的確可行,而且原文的步驟介紹已經(jīng)非常詳細(xì),這里就不再贅述。需要注意的是
MongoDB 3.2 最新版本已經(jīng)是了 wiredtiger 2.8,所以編譯 wt 工具時,可以下載 2.8 版本的 wiredtiger 源代碼。
MongoDB 默認(rèn)會對集合數(shù)據(jù)進(jìn)行 snappy 壓縮,所以一定要確保 snappy 正確安裝,在執(zhí)行 wt 工具時,通過擴(kuò)展的形式加載 snappy lib,否則運(yùn)行時會報(bào)錯。
如果需要恢復(fù)的集合很多,本文的方法效率是很低的。
第五招:從文件里提取bson文檔來恢復(fù)
MongoDB json格式的文檔,最終是以BSON (Binary json)格式持久化存儲。
假設(shè)我們有個工具叫bsonextract(有興趣的同學(xué)可以嘗試實(shí)現(xiàn)下貢獻(xiàn)到社區(qū)里,直接調(diào) BSON 的接口,實(shí)現(xiàn)起來不難),它能從一個數(shù)據(jù)文件里分析并提取出所有 BSON 格式的內(nèi)容,那么我們也就達(dá)到了恢復(fù)數(shù)據(jù)的目的。
分析時,一段數(shù)據(jù)滿足2個條件,我們即可認(rèn)為是一個合法的 MongoDB 文檔
這段數(shù)據(jù)是一個合法的 BSON 文檔
包含一個id 字段 (oplog 集合不包含id 字段,但通常也沒有去恢復(fù) oplog 的必要)
上面這個方法不僅只能恢復(fù) wiredtiger 的數(shù)據(jù),對 MongoDB 所有存儲引擎都有效。
總結(jié)
?MongoDB 一直在優(yōu)化 MongoDB ,讓它能在 repair 模式里自動處理各種數(shù)據(jù)文件損壞(或部分丟失)的場景,目標(biāo)就是萬一遇到數(shù)據(jù)集損壞的場景,repair都能自動修復(fù)掉。
下面是 repair 以后能自動處理的一些場景及處理方法
Database files missing
An entry for a file will exist in the catalogue, but on disk file is gone
Will be impossible to recover from, remove the entry from the catalogue
Warn the user strongly about this (Error message)
Database files corrupted
An entry for a file will exist in the catalogue, but on disk file is unable to be opened
Attempt to rename the collection with WiredTiger to a new table that has some mention of it being corrupted in the name
Re-create the same collection with the same name (in order to continue repair)
Warn the user strongly about this problem, the creation of the new collection
Index files missing
An entry will exist in the catalogue, but on disk file is gone
Build the index as part of repair
Index files corrupted
An entry will exist in the catalogue, but on disk file is unable to be opened
Drop, then rebuild the index as part of repair
MongoDB catalogue metadata may be out of alignment with the WT files on disk
When something is missing on disk, then this should be resolved by the changes above
When something is missing from the catalogue metadata but exists as a wt table on disk we have no recourse. We would need a user accessible function to import
If the WiredTiger metadata is corrupt, then the database is corrupt