從損壞的wt文件中恢復出WiredTiger集合


Reference: http://dev.guanghe.tv/2016/06/recovering-a-wiredtiger-collection-from-a-corrupt-wt-file.html

 

常在河邊走,哪能不濕鞋。雖然說只要不使用kill -9殺進程,一般不會導致MongoDB出問題(Mongo本身有對kill做處理),但是程序總有跑偏的時候,也許哪次服務器重啟或者遇到斷電之類的,沒准就會導致數據庫文件損壞。

當然一般的異常關閉后啟動不了時可能也就是刪除一下lock、pid文件或者tmp下的sock文件即可搞定,根本不是什么問題,偶爾的數據異常--repair也就可以了(數據量大要建一堆索引的時候慎用,等很長時間給你拋出一個修復失敗是最容易讓人崩潰的……),而且其實開啟了journal的情況下非正常關閉mongo時還有比較好用的數據文件自動修復功能,MongoDB的可靠性其實還不錯。

我們這次要處理的就是一個沒有開journal而且還遇到wt數據文件出現異常的數據庫。

根據MongoDB的啟動日志,WT_SESSION.open_cursor: unable to read root page from file:collection-3659--4168324323017494102.wt: WT_ERROR: non-specific WiredTiger error,是3659這個wt文件頭出錯了,這個要怎么辦呢?如果按照某些「專業數據庫修復」專家的建議,我們可以把這個損壞的wt文件替換掉。然后你猜會發生什么?WT_SESSION.open_cursor: collection-3659--4168324323017494102.wt read error: failed to read 4096 bytes at offset 42864640: WT_ERROR: non-specific WiredTiger error,肯定還會報錯嘛,Mongo怎么可能不對文件做校驗,暴力地替換肯定行不通啊……再往后怎么發展呢?這就到了專家們的營收環節了,只有到這一步才能體現出他們的『文件修復技術』是有價值的嘛……好了,話不多說,修復個小文件開口就四位數起步,說什么都不能忍……接下來我們自己動手,一步步開啟MongoDB數據庫修復從入門到放棄系列教程。

以下步驟可以通過直接讀取wt文件恢復出對應的集合。關於MongoDB的WT引擎數據的目錄布局可以到這里補習。

1.准備工作

1.1 wt實用工具包

wt實用工具包是本文用到的核心工具。下面是ubuntu下的操作示例,編譯過程嘛,無非就是根據報錯信息安裝一些必要的依賴,一般來只要有gcc、g++之類的編譯必備工具就OK了,唯一一個比較特殊的依賴是Google家的snappy,因為WT引擎默認的表數據壓縮方式是snappy(而不是zlib)。

wget http://source.wiredtiger.com/releases/wiredtiger-2.7.0.tar.bz2
tar xvf wiredtiger-2.7.0.tar.bz2 && cd wiredtiger-2.7.0
sudo apt-get install libsnappy-dev build-essential
./configure --enable-snappy
make

1.2 要恢復的文件

准備好要恢復的collection*****.wt,以及讀取它必備的_mdb_catalog.wtsizeStorer.wtstorage.bsonWiredTigerWiredTiger.basecfgWiredTiger.lockWiredTiger.turtleWiredTiger.wt。我們可以把這些文件放到一個新目錄,比如本例放到mongo_bak下,目錄結構如下:

collection********.wt
_mdb_catalog.wt
sizeStorer.wt
storage.bson
WiredTiger
WiredTiger.basecfg
WiredTiger.lock
WiredTiger.turtle
WiredTiger.wt

2.開始干吧

2.1 『打撈』出可以被恢復的部分

./wt -v -h ../mongo-bak -C "extensions=[./ext/compressors/snappy/.libs/libwiredtiger_snappy.so]" -R salvage collection******.wt

這一步操作會讀取我們指定的collection*****.wt,忽略所有無法被恢復的數據,然后把新數據覆蓋回去。當然你也可以修改參數讓它把salvage后的文件寫到另一個地方。

運行上述命令會輸出WT_SESSION.salvage 639400這樣的結果,后面那個數量其實就是所有能被恢復的數據。但是你現在還不能把這個直接讀取到MongoDB。

2.2 做些必要的數據格式調整

因為上一步產生的wt文件還沒法直接用MongoDB讀,所以我們接下來這幾步就利用wt的dump和load工具想辦法把他們導入到MongoDB。

2.2.1 wt --> dump

./wt -v -h ../mongo-bak -C "extensions=[./ext/compressors/snappy/.libs/libwiredtiger_snappy.so]" -R dump -f ../collection.dump collection******

這一步會把我們剛打撈出來的健康的wt文件dump到上級目錄的collection.dump文件(當然這個文件叫什么名、存哪里你自己定,下一步你還能找到就行)。注意這一步操作指定的collection不需要寫wt擴展名了,程序很貼心有木有,省下3次敲鍵盤的體力可以干好多事情呢……我知道你都是復制粘貼的……

還要注意的是這一步程序是沒有任何狀態輸出的(如果你看到了估計肯定是錯誤提示……),如果想看到進度的話可以用shell中的ls -l等命令通過觀察collection.dump文件的變化來揣測進度,以及帶給你一些程序確實運行成功了的安全感。

2.2.2 a new collection

這一步主要是為接下來的load做准備:我們要建立一個新的數據庫,然后把上一步dump的數據導入進來,然后還有幾個關鍵而鬼畜的步驟,后面都會提到,所以這一步還是老老實實跟着做一下吧。

來,我們先啟動一個新的Mongo實例,我舉個栗子,可以這么做:

mongod --dbpath tmp-mongo --storageEngine wiredTiger --nojournal

然后我們要連接這個實例並創建一個新集合

mongo
> use Recovery
> db.brokedCollection.insert({test: 1})
> db.brokedCollection.remove({})
> db.brokedCollection.stats()

我們創建了一個新的叫做Recovery的數據庫,並且插入移除過文檔,所以這些集合的數據文件會被生成。使用stats()方法可以查看集合所對應的wt文件名稱,當然了,因為我們只使用了一個集合,所以跑到tmp-mongo目錄下ls一下也就知道這個collecion對應的wt文件是哪個了……為什么要知道這個?當然是下一步要用了……

2.2.3 dump --> new wt

接下來是見證奇跡的時刻:我們的數據很快就可以重現Mongo了!

./wt -v -h ../tmp-mongo -C "extensions=[./ext/compressors/snappy/.libs/libwiredtiger_snappy.so]" -R load -f ../collection.dump -r collection******

這一步就是把前面轉出來的dump文件讀入上一步生成的collection文件,所以-h指定的當然是我們上一步用的新mongo實例的路徑。執行這一步的時候需要先把Mongo關上,不然mongo進程會霸占着這個wt文件不讓你操作。

這個操作是有一個進度展示的table:collection-******: 1386220

來來來,見證一下奇跡:現在可以再用2.2.2里的方式啟動這個Mongo實例了

mongo
> show dbs              //應該可以看到Recovery有數據了
> use Recovery
> show collections      //應該可以看到brokedCollection里有數據了
> db.brokedCollection.count()   //是0?吶尼?

看到0的那一刻估計你又開始緊張了,別急我們慢慢來,如果到這一步就能解決問題的話我們何苦要折騰出2.2.2這一步。

> db.brokedCollection.find({}, {_id: 1})

接着執行這一條能看到數據,所以我估計你會再次燃起希望,接下來我們繼續,讓奇跡2出現吧:

2.3 完善一下

需要注意為了防止出錯執行下面的步驟要確保MongoDB版本不小於3.2(因為3.2版本才是基於WiredTiger2.7構建的)。

mongodump
mongorestore --drop

沒錯就是這么簡單,接下來我們就可以驗證奇跡了。

mongo
> show dbs
> use Recovery
> show collections
> db.brokedCollection.count()

這次都回來了吧,一切正常了吧……接下來可以restore到任何你想要restore的地方了,如何使用可自由發揮。

3 收工

收工吧,累死了。

也許你已經受到上面步驟的啟發想到更多有意思的恢復方法了,然而人生苦短,適可而止……

MongoDB使用建議

對重要數據庫所在機器操作前一定要提前backup一下,把萬萬沒想到的損失降到最低;還有,除非真的不care數據的高可用性,不要隨便關journal;單台機器上有多個庫的話最好在配置文件中設置下directoryPerDB:true,讓每個庫有一個單獨文件夾;有富余機器的話盡量做一下復制集……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM