生產事故(MongoDB數據分佈不均解決方案)

事故集合:node

能夠很明顯能夠看到咱們這個集合的數據嚴重分佈不均勻。mongodb

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

形成這次生產事故的首要緣由就是片鍵選擇上的問題,因爲片鍵選擇失誤,在數據量級不大的時候數據看起來仍是很健康的,但隨着數據量的暴漲,問題就慢慢浮出了水面,咱們使用的組合片鍵並非無規律的,片鍵內容是線性增加的,這就致使了數據的不正常彙集。因爲數據分佈不均勻,咱們有兩個分片的磁盤使用率接近80%,數據還在持續增加,這個問題必須儘快解決。

涉及到這次事故的集合一共有三個,總數據量加起來接近30T,數據總量300億左右。服務器

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

方案一:性能

第一步:建立一個新的分片表,片鍵我選擇_id作hashed分片,並提早分好了數據塊,下降在恢復期間頻繁切割數據形成的服務器壓力。ui

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作數據備份時出現瞭如下異常:rest

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。

日誌內容大體以下:

2019-09-24T18:51:39.003+0800 I -        [conn33]   Index Build: 838416900/876543270 95%
2019-09-24T20:10:08.360+0800 I INDEX    [conn33] 	 done building bottom layer, going to commit
2019-09-24T20:10:26.001+0800 I -        [conn33]   Index: (2/3) BTree Bottom Up Progress: 11684400/876543270 1%
done building bottom layer, going to commit
相關文章
相關標籤/搜索