天天看點

mongodb 生産_生産事故(MongoDB資料分布不均解決方案)

可以很明顯可以看到我們這個集合的資料嚴重分布不均勻。一共有8個分片,面對這個情況我首先想到的是手動拆分資料塊,但這不是解決此問題的根本辦法。

作者:7010G 來源:開源中國

事故集合:

mongodb 生産_生産事故(MongoDB資料分布不均解決方案)

可以很明顯可以看到我們這個集合的資料嚴重分布不均勻。

一共有8個分片,面對這個情況我首先想到的是手動拆分資料塊,但這不是解決此問題的根本辦法。

造成此次生産事故的首要原因就是片鍵選擇上的問題,由于片鍵選擇失誤,在資料量級不大的時候資料看起來還是很健康的,但随着資料量的暴漲,問題就慢慢浮出了水面,我們使用的組合片鍵并不是無規律的,片鍵内容是線性增長的,這就導緻了資料的不正常聚集。由于資料分布不均勻,我們有兩個分片的磁盤使用率接近80%,資料還在持續增長,這個問題必須盡快解決。

涉及到此次事故的集合一共有三個,總資料量加起來接近30T,資料總量300億左右。

下面是我解決此問題的解決方案:

方案一:

第一步:建立一個新的分片表,片鍵我選擇_id做hashed分片,并提前分好了資料塊,降低在恢複期間頻繁切割資料造成的伺服器壓力。

sh.shardCollection("loan_his.collection",{_id:"hashed"},false,{numInitialChunks:1024})

第二步:單獨連接配接各個分片将8個分片的資料全量備份:

nohup mongodump -u loan_his -p loan_his --authenticationDatabase loan_his -h ${replset} --db loan_his --collection ${collectionName} --query '{"txdt": { $lte: "2019-07-09"} }' -o ${bak_dir} &>> ${log} &

你可能會問為什麼不連接配接mongos,因為我在連接配接mongos做資料備份時出現了以下異常:

2019-07-08T16:10:03.886+0800 Failed: error writing data for collection `loan_his.ods_cus_trad` to disk: error reading collection: operation was interrupted

可能是因為集合内的資料壞塊吧,此異常資訊是我備份了将近70%的資料後突然抛出的異常資訊。

除了這個原因,單獨備份各個分片的資料後你能夠自由控制恢複資料的時間視窗,不會因為恢複單個資料檔案時間較長,突發意外情況導緻恢複中斷從頭再來的窘境。能夠根據伺服器的狀态避開高峰期來進行資料恢複。

備份期間我發現了有時候備份出來的總文檔數和 db.collection.getShardDistribution() 檢視的文檔數不一緻,我還以為是備份期間出了問題,但我删除目前備份檔案後重新備份出來的文檔數還是和之前一樣。目前不知道是怎麼回事,懷疑是壞的資料塊引發的我問題,備份出來的資料一般會比原資料量多幾萬條資料,有時候會少一些。

第三步:恢複資料:

mongorestore -u loan_his -p loan_his --authenticationDatabase loan_his -h 10.0.156.9:27017 --db loan_his --collection ${collectionName_two} /mongodb/${collectionName}/replset_sh2/loan_his/${collectionName}.bson &>> ${log}

在恢複資料前千萬要記得不要建立索引!否則性能極差,速度非常非常慢!在使用mongodump工具備份時,在資料檔案的同級目錄下會有一個 XXXXX.metadata.json 索引檔案,預設會在資料恢複完畢後執行建立索引的操作。

此處有坑需要注意:因為備份出來的資料是由原表備份出來的,那這個索引檔案也是原表的索引,由于原表我使用的是組合片鍵做的分片,是以在原表内會存在一個由片鍵組成的組合索引,并且不是背景建立的組合索引!!!這意味着如果你使用此索引檔案來給新表建立索引,會造成這個叢集處于阻塞狀态,無法響應任何操作!!直至索引建立完畢。是以你可以将這個索引檔案備份到其它目錄以作參考,然後将原檔案删除就可以了,恢複資料時不會有其它的問題。

如果恢複期間出現了意外情況導緻恢複失敗,比如節點當機什麼的,不需要擔心,重新執行恢複程式,資料檔案不會重複增加,因為備份出來的資料檔案包含mongodb自帶的 Objectld對象_id ,導入時,如果已存在此ID,将不會插入資料。注意:在不同集合是允許出現相同ID的,是以在使用方案二恢複資料時,新産生的資料不能通過新表A備份出來彙入新表C,需要通過原始資料檔案重新導入。

第四步:建立索引:

待所有資料恢複完畢後再建立索引,一定要記得背景建立!!!"background":true !!!你也可以将索引拆分,一個一個的來。如果覺得此操作對業務影響較大,請看本文最後的解決方案。

mongo 10.0.156.2:27017/loan_his -uloan_his -ploan_his -eval 'db.getSiblingDB("loan_his").runCommand({createIndexes: "collection",indexes: [{"v":2,"key":{"_id":1},"name":"_id_","ns":"loan_his.collection"},{"v":2,"key":{"opnode":1.0,"txdt":1.0,"acct":1.0,"crdno":1.0},"name":"opnode_1_txdt_1_acct_1_crdno_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"txdt":1.0,"opnode":1.0,"acct":1.0,"crdno":1.0,"pbknum":1.0},"name":"txdt_1_opnode_1_acct_1_crdno_1_pbknum_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"acct":1.0,"txdt":1.0,"opnode":1.0},"name":"acct_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"crdno":1.0,"txdt":1.0,"opnode":1.0},"name":"crdno_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"pbknum":1.0,"txdt":1.0,"opnode":1.0},"name":"pbknum_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true}]})'

停止失控索引:

一旦你觸發一個索引,簡單的重新開機服務并不能解決這個問題,因為MongoDB會繼續重新開機前的建索引的工作。如果之前你運作背景建索引任務,在服務重新開機後它會變成前台運作的任務。在這種情況下,重新開機會讓問題變得更糟糕。MongoDB提供了選項“noIndexBuildRetry”,它會訓示MongoDB重新開機後不再繼續沒建完的索引。如果不小心在前台建立了索引導緻叢集不可用,可以使用--noIndexBuildRetry 參數重新開機各個分片來停止索引的建立過程,隻用重新開機主節點就可以了。如果是在背景建立索引,重新開機時記得加上--noIndexBuildRetry,否則重新開機後建立索引的線程會重新被喚醒,并由背景建立變為前台建立,導緻整個叢集不可用。

mongod -f $CONFIGFILE --noIndexBuildRetry

此方案遷移期間不用通知業務系統做變更,把資料遷移完畢後,通知業務系統将表名變更,弊端就是在你遷移的過程中資料還是會持續增長的,問題分片的磁盤容量會越來越少。

方案二:

為了避免在遷移期間資料仍在增長,導緻資料還沒遷移完畢磁盤就爆滿的情況,可以選擇停止往舊表B内寫入資料,建立一個健康的新表A,新的資料往新表A内寫,具體的查詢方案需要應用系統的配合。然後将舊表B的資料遷移至新表C中,最終将新表A的資料彙入新表C , 完成資料遷移。此次遷移資料耗時共9個月!!!片鍵一定要慎重選擇,因為我們使用的MongoDB是3.4.7版本的,不支援修改片鍵,最新版本支援片鍵的修改。

接下來介紹資料量較大時如何建構索引--減少業務最少影響

在資料量較大或請求量較大,直接建立索引對性能有顯著影響時,可以利用複制集(資料量較大時一般為線上環境,使用複制集為必然選擇或者使用分片.)中部分機器當機不影響複制集工作的特性,繼而建立索引。

(1)首先把 secondary server 停止,再注釋 --replSet 參數,并且更改 MongoDB port 之後重新啟動 MongoDB,這時候 MongoDB 将進入 standalone 模式;

(2).在 standalone 模式下運作指令 ensureIndex 建立索引,使用 foreground 方式運作也可以,建議使用background方式運作;

(3)建立索引完畢之後關閉 secondary server 按正常方式啟動;

4.根據上述 1~3 的步驟輪流為 secondary 建立索引,最後把 primary server 臨時轉換為 secondary server,同樣按 1~3 的方法建立索引,再把其轉換為 primary server。

日志内容大緻如下:

mongodb 生産_生産事故(MongoDB資料分布不均解決方案)