提高 MongoDB 運維效率的實用技巧
一、MongoDB 叢集簡介
MongoDB是一個基於分散式檔案儲存的資料庫,其目的在於為WEB應用提供可擴充套件的高效能資料儲存解決方案。下面將以3臺機器介紹最常見的叢集方案。具體介紹,可以檢視官網 。
1、叢集元件的介紹
-
mongos(路由處理):作為Client與MongoDB叢集的請求入口,所有使用者請求都會透過Mongos協調,它會將資料請求發到對應的Shard(mongod)伺服器上,再將資料合併後回傳給使用者。 -
config server(配置節點):即:配置伺服器;主要儲存資料庫的後設資料,包含資料的分佈(分片)以及資料結構,mongos收到client發出的需求後,會從config server載入配置資訊並快取於記憶體中。一般在生產環境會配置不只一臺config server,因為它儲存的後設資料極為重要,若損壞則影響整個叢集運作。 -
shard(分片例項儲存資料):shard就是分片。MongoDB利用分片的機制來實現資料分佈儲存與處理,達到橫向擴容的目的。預設情況下,資料在分片之間會自動進行移轉,以達到平衡,此動作是靠一個叫平衡器(balancer)的機制達成。 -
replica set(副本集):副本集實現了資料庫高可用,若沒做副本集,則一旦存放資料的伺服器節點掛掉,資料就丟失了,相反若配置了副本集,則同樣的資料會儲存在副本伺服器中(副本節點),一般副本集包含了一個主節點與多個副本節點,必要時還會配置arbiter(仲裁結點)作為節點掛掉時投票用。 -
arbiter(仲裁節點):仲裁伺服器本身不包含資料,僅能在主節點故障時,檢測所有副本伺服器並選舉出新的主節點,其實現方式是透過主節點、副本節點、仲裁伺服器之間的心跳(Heart beat)實現。
2、MongoDB應用場景
-
網站資料:適合實時的插入,更新與查詢,並具備網站實時資料儲存所需的複製及高度伸縮性。 -
快取:由於效能很高,也適合作為資訊基礎設施的快取層。在系統重啟之後,搭建的持久化快取可以避免下層的資料來源過載。 -
大尺寸、低價值的資料:使用傳統的關聯式資料庫儲存一些資料時可能會比較貴,在此之前,很多程式設計師往往會選擇傳統的檔案進行儲存。 -
高伸縮性的場景:非常適合由數十或者數百臺伺服器組成的資料庫。 -
用於物件及JSON資料的儲存:MongoDB的BSON資料格式非常適合文件格式化的儲存及查詢。
3、選用MongoDB的緣由
選用MongoDB的資料是以BSON的資料格式,高度伸縮方便擴充套件,並且資料水平擴充套件非常簡單,支援海量資料儲存,效能強悍。
二、叢集的監測
1、監測資料庫儲存統計資訊
docker中進入mongos或shard例項,執行以下命令:
docker exec -it mongos bash;
mongo --port 20001;
use admin;
db.auth("root","XXX");
說明:透過此命令,可以查詢叢集的成員的集合數量、索引數量等相關資料。
db.stats();
2、檢視資料庫的統計資訊
說明:透過此命令,可以檢視運算元量、記憶體使用狀況、網路io等
db.runCommand( { serverStatus: 1 } );
3、檢查複製整合員狀態
rs.status();
三、基本的運維操作
1、設定和檢視慢查詢
#
設定慢查詢
db.setProfilingLevel(1,200);
#
檢視慢查詢級別
db.getProfilingLevel();
#
查詢慢查詢日誌,此命令是針對於某一庫進行設定
db.system.profile.find({ ns : 'dbName.collectionName'}).limit(10).sort( { ts : -1 } ).pretty();
2、檢視執行操作時間較長的動作
db.currentOp({"active" : true,"secs_running" : { "$gt" : 2000 }});
3、動態調整日誌級別和設定快取大小
#
設定日誌級別引數
db.adminCommand( { "getParameter": 1, "logLevel":1});
#
設定cache大小引數
db.adminCommand( { "setParameter": 1, "wiredTigerEngineRuntimeConfig": "cache_size=4G"});
4、新增和移除複製整合員
#
檢視複製整合員
rs.status().members;
#
新增成員
rs.add('127.0.0.1:20001');
#
移除成員
rs.remove('127.0.0.1:20001');
5、設定資料庫和集合分片
#
在mongos admin庫設定庫允許分片
sh.enableSharding("dbName");
#
在mongos 的admin庫設定集合分片片鍵
sh.shardCollection("dbName.collectionName", { filedName: 1} );
6、新增和移除分片
#
檢視分片狀態
sh.status();
#
在mongos執行新增分片(可以為單個例項或複製集)
db.runCommand( { removeShard: "shardName" } );
db.runCommand({addshard:"rs1/ip-1:20001,ip-2:20001,ip-3:20001"});
#
在mongos執行移除分片
db.runCommand( { removeShard: "shard3" } );
#
在mongos執行重新整理mongos配置資訊
db.runCommand("flushRouterConfig"));
說明:移除分片命令至少執行兩次才能成功刪除,執行到state為completed才真正刪除,否則就是沒用刪除成功,該分片處於
{"draining" : true}
狀態,該狀態下不但該分片沒用刪除成功,而且還影響接下來刪除其他分片操作,遇到該狀態再執行一次
removeshard
即可,最好就是刪除分片時一直重複執行刪除命令,直到state為completed; 還有一個需要注意的地方就是:被成功刪除的分片如果想要再加入叢集時,必須將data資料目錄清理乾淨才可以再加入叢集,否則即使能加入成功也不會儲存資料,集合都不會被建立 另外:在刪除分片的時有可能整個過程出現無限
{"draining" : true}
狀態,等多久還是這樣,而且分片上面的塊一個都沒有移動到別的分片,解決辦法是:在config的config資料庫的shard集合中找到該分片的資訊,並將draining欄位由True改為False,再繼續試著刪除操作” 上面這句會立即返回,實際在後臺執行。 在資料移除的過程當中,一定要注意例項的日誌資訊,可能出現資料塊在遷移的過程中,始終找不到邊界條件,導致一直資料遷移不成功,一直重試,解決方案是刪除邊界資料,重啟例項;。如果此分片為主分片,需要先遷移主分片。
db.runCommand( { movePrimary: "XXX", to: "other" });
在完成刪除後,所有mongos上執行下面命令,再對外提供服務,當然也可以重新啟動所有mongos例項 。
7、資料的匯入匯出
#
匯出允許指定匯出條件和欄位
mongoexport -h 127.0.0.1 --port 20001 -uxxx -pxxx -d xxx -c mobileIndex -o XXX.txt
mongoimport -h 127.0.0.1 --port 20001 -uxxx -pxxx -d xxx -c mobileIndex --file XXX.txt
四、MongoDB資料遷移
1、遷移複製集當中的成員
-
關閉 mongod 例項,為了確保安全關閉,使用 shutdown 命令; -
將資料目錄(即 dbPath )轉移到新機器上; -
在新機器上啟動 mongod,其中節點的資料目錄為copy的檔案目錄 ; -
連線到複製集當前的主節點上;
如果新節點的地址發生變化,使用 rs.reconfig() 更新 複製集配置文件 ;舉例,下面的命令過程將成員中位於第 2 位的地址進行更新:
cfg = rs.conf()
cfg.members[2].host =
"127.0.0.1:27017"
rs.reconfig(cfg)
使用
rs.conf()
確認使用了新的配置. 等待所有成員恢復正常,使用
rs.status()
檢測成員狀態。
2、遷移複製集主節點
在遷移主節點的時候,需要複製集選舉出一個新的主節點,在進行選舉的時候,複製集將讀寫,通常,這隻會持續很短的時間,不過,應該儘可能在影響較小的時間段內遷移主節點.
-
主節點降級,以使得正常的 failover開始.要將主節點降級,連線到一個主節點,使用 replSetStepDown
方法或者使用rs.stepDown()
方法,下面的例子使用了rs.stepDown()
方法進行降級:
rs.stepDown()
-
等主節點降級為從節點,另一個成員成為 PRIMARY
之後,可以按照 “遷移複製集的一個成員”遷移這個降級了的節點.可以使用rs.status()
來確認狀態的改變。
3、從複製集其他節點恢復資料
MongoDB 透過複製集能保證高可靠的資料儲存,通常生產環境建議使用「3節點複製集」,這樣即使其中一個節點崩潰了無法啟動,我們可以直接將其資料清掉,重新啟動後,以全新的 Secondary 節點加入複製集,或者是將其他節點的資料複製過來,重新啟動節點,它會自動的同步資料,這樣也就達到了恢復資料的目的。
-
關閉需要資料同步的節點
docker stop node; # docker環境中
db.shutdownServer({timeoutSecs: 60}); # 非docker環境
-
複製目標節點機器的資料儲存目錄(/dbPath)到當前機器的指定目錄。
scp 目標節點 shard/data -> 當前節點 shard/data
-
當前節點以複製過來的資料檔案啟動節點 -
將新的節點新增到複製集
#
進入複製集的主節點,執行新增新的節點命令
rs.add("hostNameNew:portNew");
#
等待所有成員恢復正常,檢測成員狀態
rs.status();
#
移除原來的節點
rs.remove("hostNameOld>:portOld");
五、MongoDB線上問題場景解決
1、MongoDB 新建索引導致庫被鎖
問題說明:某線上千萬級別集合,為最佳化業務,直接執行新建索引命令,導致整個庫被鎖,應用服務出現不可用。
解決方案:找出此操作程式,並且殺死。改為後臺新建索引,速度很會慢,但是不會影響業務,該索引只會在新建完成之後,才會生效;
#
查詢執行時間超過200ms操作
db.currentOp({"active" : true,"secs_running" : { "$gt" : 2000 }}) ;
#
殺死執行時間過長操作操作
db.killOp(opid)
#
後臺新建索引
db.collectionNmae.ensureIndex({filedName:1}, {background:true});
2、MongoDB沒有限制記憶體,導致例項退出
問題說明:生產環境某臺機器啟動多個mongod例項,執行一段時間過後,程式莫名被殺死;
解決方案:現在MongoDB使用WiredTiger作為預設儲存引擎,MongoDB同時使用WiredTiger內部快取和檔案系統快取。從3.4開始,WiredTiger內部快取預設使用較大的一個:50%(RAM - 1 GB),或256 MB。例如,在總共4GB RAM的系統上,WiredTiger快取將使用1.5GB的RAM()。相反,具有總共1.25 GB RAM的系統將為WiredTiger快取分配256 MB,因為這超過總RAM的一半減去1千兆位元組()。0.5 * (4 GB - 1GB) = 1.5 GB``0.5 * (1.25 GB - 1 GB) = 128 MB < 256 MB。如果一臺機器存在多個例項,在記憶體不足的情景在,作業系統會殺死部分程式;
#
要調整WiredTiger內部快取的大小,調節cache規模不需要重啟服務,我們可以動態調整:
db.adminCommand( { "setParameter": 1, "wiredTigerEngineRuntimeConfig": "cache_size=xxG"})
3、MongoDB刪除資料,不釋放磁碟空間
問題說明:在刪除大量資料(本人操作的資料量在2000萬+)的情景下,並且在生產環境中請求量較大,此時機器的cpu負載會顯得很高,甚至機器卡頓無法操作,這樣的操作應該謹慎分批次操作;在刪除命令執行結束之後,發現磁碟的資料量大小並沒有改變。
解決方案:
-
方案一:我們可以使用MongoDB提供的線上資料收縮的功能,透過Compact命令
db.collectionName.runCommand("compact")
進行Collection級別的資料收縮,去除集合所在檔案碎片。此命令是以Online的方式提供收縮,收縮的同時會影響到線上的服務。為了解決這個問題,可以先在從節點執行磁碟整理命令,操作結束後,再切換主節點,將原來的主節點變為從節點,重新執行Compact命令即可。 -
方案二:使用從節點重新同步,secondary節點重同步,刪除secondary節點中指定資料,使之與primary重新開始資料同步。當副本整合員資料太過陳舊,也可以使用重新同步。資料的重新同步與直接複製資料檔案不同,MongoDB會只同步資料,因此重同步完成後的資料檔案是沒有空集合的,以此實現了磁碟空間的回收。
針對一些特殊情況,不能下線secondary節點的,可以新增一個節點到副本集中,然後secondary就自動開始資料的同步了。總的來說,重同步的方法是比較好的,第一基本不會阻塞副本集的讀寫,第二消耗的時間相對前兩種比較短。
-
若是primary節點,先強制將之變為secondary節點,否則跳過此步驟: rs.stepdown(120);
-
然後在primary上刪除secondary節點: rs.remove("IP:port");
-
刪除secondary節點dbpath下的所有檔案 -
將節點重新加入叢集,然後使之自動進行資料的同步: rs.add("IP:port");
-
等資料同步完成後,迴圈1-4的步驟可以將叢集中所有節點的磁碟空間釋放
4、MongoDB機器負載極高
問題說明:此情景是在客戶請求較大的情景性,由於部署MongoDB的機器包含一主一從,MongoDB使得IO100%,資料庫阻塞,出現大量慢查詢,進而導致機器負載極高,應用服務完全不可用。
解決方案:在沒有機器及時擴容的狀況下,首要任務便是減小機器的IO,在一臺機器出現一主一從,在大量資料寫入的情況下,會互相搶佔IO資源。於是此時摒棄了MongoDB高可用的特點,摘掉了複製集當中的從節點,保證每臺機器只有一個節點可以佔用磁碟資源。之後,機器負載立馬下來,服務變為正常可用狀態,但是此時MongoDB無法保證資料的完整性,一旦有主節點掛掉便會丟失資料。此方案只是臨時方法,根本解決是可以增加機器的記憶體、使用固態硬碟,或者採用增加分片集來減少單個機器的讀寫壓力。
#
進入主節點,執行移除成員的命令
rs.remove("127.0.0.1:20001");
#
注意:切勿直接關停例項
5、MongoDB分片鍵選擇不當導致熱讀熱寫
問題說明:生產環境中,某一集合的片鍵使用了與_id生成方式相似,含有時間序列的欄位作為升序片鍵,導致資料寫入時都在一個資料塊,隨著資料量增大,會造成資料遷移到前面的分割槽,造成系統資源的佔用,偶爾出現慢查詢。
解決方案:臨時方案設定資料遷移的視窗,放在在正常的時間區段,對業務造成影響。根本解決是更換片鍵。
#
連線mongos例項,執行以下命令
db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "23:00", stop : "4:00" } } }, true );
#
檢視均衡視窗
sh.getBalancerWindow();
六、MongoDB最佳化建議
1、應用層面最佳化
查詢最佳化:確認你的查詢是否充分利用到了索引,用explain命令檢視一下查詢執行的情況,新增必要的索引,避免掃表操作。
合理設計分片鍵:增量sharding-key:適合於可劃分範圍的欄位,比如integer、float、date型別的,查詢時比較快。隨機sharding-key: 適用於寫操作頻繁的場景,而這種情況下如果在一個shard上進行會使得這個shard負載比其他高,不夠均衡,故而希望能hash查詢key,將寫分佈在多個shard上進行,考慮複合key作為sharding key, 總的原則是查詢快,儘量減少跨shard查詢,balance均衡次數少;單一遞增的sharding key,可能會造成寫資料全部在最後一片上,最後一片的寫壓力增大,資料量增大,會造成資料遷移到前面的分割槽。MongoDB預設是單條記錄16M,尤其在使用GFS的時候,一定要注意shrading-key的設計。不合理的sharding-key會出現,多個文件,在一個chunks上,同時,因為GFS中存貯的往往是大檔案,導致MongoDB在做balance的時候無法透過sharding-key來把這多個文件分開到不同的shard上, 這時候MongoDB會不斷報錯最後導致MongoDB倒掉。解決辦法:加大chunks大小(治標),設計合理的sharding-key(治本)。
透過profile來監控資料:進行最佳化檢視當前是否開啟profile功能 用命令
db.getProfilingLevel()
返回level等級,值為0|1|2,分別代表意思:0代表關閉,1代表記錄慢命令,2代表全部。開啟profile功能命令為
db.setProfilingLevel(level);
#level等級,值level為1的時候,慢命令預設值為100ms,更改為
db.setProfilingLevel(level,slowms)如db.setProfilingLevel(1,50)
這樣就更改為50毫秒透過
db.system.profile.find()
檢視當前的監控日誌。
2、硬體層面最佳化
2.1 確定熱資料大小:可能你的資料集非常大,但是這並不那麼重要,重要的是你的熱資料集有多大,你經常訪問的資料有多大(包括經常訪問的資料和所有索引資料)。使用MongoDB,你最好保證你的熱資料在你機器的記憶體大小之下,保證記憶體能容納所有熱資料;2.2 選擇正確的檔案系統:MongoDB的資料檔案是採用的預分配模式,並且在Replication裡面,Master和Replica Sets的非Arbiter節點都是會預先建立足夠的空檔案用以儲存操作日誌。這些檔案分配操作在一些檔案系統上可能會非常慢,導致程式被Block。所以我們應該選擇那些空間分配快速的檔案系統。這裡的結論是儘量不要用ext3,用ext4或xfs;
3、架構上的最佳化
儘可能讓主從節點分攤在不同的機器上,避免IO操作的與MongoDB在同一臺機器;
七、總結
MongoDB具有高效能、易擴充套件、易上手等特點,在正確使用的情況下,其本身效能還是非常強悍,在一些關鍵點如片鍵的選擇、記憶體的大小和磁碟IO,往往是限制其效能的最大瓶頸。針對於片鍵,在業務系統初期,可以先不對集合進行資料分片,因為分片鍵一旦確定就無法修改,後期可根據業務系統的情況,認真篩選欄位。
一般情況下,不建議使用升序片鍵(是一種隨著時間穩定增長的欄位,自增長的主鍵是升序鍵 ),因為這個會導致區域性的熱讀熱寫,不能發揮分片叢集的真正實力。建議使用hash片鍵或者隨機分發的片鍵,這樣可以保證資料的均勻分發在分片節點;針對於記憶體,建議記憶體的大小能夠包含熱資料的大小加索引大小,保證記憶體能容納所有熱資料 。針對於磁碟資源,MongoDB的高速讀寫是以磁碟的IO作為基礎,為了保證其效能,建議將主從節點以及高IO的應用分離,以保證IO資源儘可能不存在搶佔。
原文連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70013542/viewspace-2946696/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 用行雲管家實現IT統一運維管理,提高運維效率運維
- 運維工作實用技巧運維
- 實用技術乾貨!教你用機器學習提高日常安全運維工作中的效率機器學習運維
- 分享一個提高運維效率的 Python 指令碼運維Python指令碼
- 運維乾貨 | 12 個實用的 Vim 使用技巧運維
- 提高開發效率小技巧
- 提高 Linux 運維效率的 30 個命令列常用快捷鍵Linux運維命令列
- 程式設計技巧│提高 Javascript 程式碼效率的技巧程式設計JavaScript
- excel實用技能:提高工作效率的6個excel技巧,助你事半功倍Excel
- 京東雲開發者|提高IT運維效率,深度解讀京東雲AIOps落地實踐運維AI
- 超實用的乾貨分享給大家,提高工作效率的Excel技巧!Excel
- 提高VSCode 10倍效率的技巧VSCode
- 2022年提高遠端工作效率的三大實用技巧彙總
- 5個Excel實用技巧,幫你大大提高工作效率!Excel
- 4個Excel技巧,提高你的工作效率!Excel
- 提高Python執行效率的5個技巧!Python
- 【譯】十個提高你寫作效率的技巧
- 提高js執行效率的幾個常用技巧JS
- 解析提高PHP執行效率的50個技巧PHP
- 【EXPDP】運用PARALLEL 和FILESIZE引數提高備份效率Parallel
- 提高開發效率的 Eclipse 實用操作(2)Eclipse
- 實戰心得!四個幫你提高UI設計效率的工作技巧UI
- 推薦給“懶惰” Linux 運維工程師的 10 個關鍵技巧,瞬間提升運維效率!Linux運維工程師
- 運維前線:一線運維專家的運維方法、技巧與實踐1.3 運維自動化的困境和價值運維
- 讓你提高效率的Linux技巧Linux
- 讓你提高效率的 Linux 技巧Linux
- 提高Python執行效率的5個小技巧!Python
- 提高python開發效率的10個小技巧Python
- VS提高實戰效率
- 能夠提高開發效率的 Eclipse 實用操作Eclipse
- 能夠提高開發效率的Eclipse實用操作Eclipse
- 安全運維小技巧運維
- 如何能提高CSS編寫技巧 提高Web前端開發效率CSSWeb前端
- 用 FutureBuilder 提高開發效率Rebuild
- 小程式助力提升運維效率運維
- 提高Linux工作效率的十大bash技巧Linux
- 程式設計師:提高程式設計效率的技巧程式設計師
- MongoDB日常運維-02安裝MongoDB運維