MongoDB是一個基於分佈式文件存儲的數據庫,其目的在於爲WEB應用提供可擴展的高性能數據存儲解決方案。下面將以3臺機器介紹最多見的集羣方案。具體介紹,能夠查看官網 https://docs.mongodb.com/v3.4...。node
mongos(路由處理):做爲Client與MongoDB集羣的請求入口,全部用戶請求都會透過Mongos協調,它會將數據請求發到對應的Shard(mongod)服務器上,再將數據合併後回傳給用戶。程序員
config server(配置節點):即:配置服務器;主要保存數據庫的元數據,包含數據的分佈(分片)以及數據結構,mongos收到client發出的需求後,會從config server加載配置信息並緩存於內存中。通常在生產環境會配置不僅一臺config server,由於它保存的元數據極爲重要,若損壞則影響整個集羣運做。mongodb
shard(分片實例存儲數據):shard就是分片。MongoDB利用分片的機制來實現數據分佈存儲與處理,達到橫向擴容的目的。默認狀況下,數據在分片之間會自動進行移轉,以達到平衡,此動做是靠一個叫平衡器(balancer)的機制達成。docker
replica set(副本集):副本集實現了數據庫高可用,若沒作副本集,則一旦存放數據的服務器節點掛掉,數據就丟失了,相反若配置了副本集,則一樣的數據會保存在副本服務器中(副本節點),通常副本集包含了一個主節點與多個副本節點,必要時還會配置arbiter(仲裁節點)做爲節點掛掉時投票用。數據庫
arbiter(仲裁節點):仲裁服務器自己不包含數據,僅能在主節點故障時,檢測全部副本服務器並選舉出新的主節點,其實現方式是經過主節點、副本節點、仲裁服務器之間的心跳(Heart beat)實現。緩存
網站數據:適合實時的插入,更新與查詢,並具有網站實時數據存儲所需的複製及高度伸縮性。安全
緩存:因爲性能很高,也適合做爲信息基礎設施的緩存層。在系統重啓以後,搭建的持久化緩存能夠避免下層的數據源過載。bash
大尺寸、低價值的數據:使用傳統的關係數據庫存儲一些數據時可能會比較貴,在此以前,不少程序員每每會選擇傳統的文件進行存儲。服務器
高伸縮性的場景:很是適合由數十或者數百臺服務器組成的數據庫。網絡
用於對象及JSON數據的存儲:MongoDB的BSON數據格式很是適合文檔格式化的存儲及查詢。
選用MongoDB的數據是以BSON的數據格式,高度伸縮方便擴展,而且數據水平擴展很是簡單,支持海量數據存儲,性能強悍。
docker中進入mongos或shard實例,執行如下命令:
docker exec -it mongos bash; mongo --port 20001; use admin; db.auth("root","XXX");
說明:經過此命令,能夠查詢集羣的成員的集合數量、索引數量等相關數據。13個Mongodb GUI可視化管理工具,總有一款適合你
db.stats();
說明:經過此命令,能夠查看操做數量、內存使用情況、網絡io等
db.runCommand( { serverStatus: 1 } );
rs.status();
# 設置慢查詢 db.setProfilingLevel(1,200); # 查看慢查詢級別 db.getProfilingLevel(); # 查詢慢查詢日誌,此命令是針對於某一庫進行設置 db.system.profile.find({ ns : 'dbName.collectionName'}).limit(10).sort( { ts : -1 } ).pretty();
db.currentOp({"active" : true,"secs_running" : { "$gt" : 2000 }});
# 設置日誌級別參數 db.adminCommand( { "getParameter": 1, "logLevel":1}); # 設置cache大小參數 db.adminCommand( { "setParameter": 1, "wiredTigerEngineRuntimeConfig": "cache_size=4G"});
# 查看複製集成員 rs.status().members; # 添加成員 rs.add('127.0.0.1:20001'); # 移除成員 rs.remove('127.0.0.1:20001');
# 在mongos admin庫設置庫容許分片 sh.enableSharding("dbName"); # 在mongos 的admin庫設置集合分片片鍵 sh.shardCollection("dbName.collectionName", { filedName: 1} );
# 查看分片狀態 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實例 。
# 導出容許指定導出條件和字段 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
若是新節點的地址發生變化,使用 rs.reconfig() 更新 複製集配置文檔 ;舉例,下面的命令過程將成員中位於第 2 位的地址進行更新:
cfg = rs.conf() cfg.members[2].host = "127.0.0.1:27017" rs.reconfig(cfg)
使用 rs.conf() 確認使用了新的配置. 等待全部成員恢復正常,使用 rs.status() 檢測成員狀態。
在遷移主節點的時候,須要複製集選舉出一個新的主節點,在進行選舉的時候,複製集將讀寫,一般,這隻會持續很短的時間,不過,應該儘量在影響較小的時間段內遷移主節點.
主節點降級,以使得正常的 failover開始.要將主節點降級,鏈接到一個主節點,使用 replSetStepDown方法或者使用rs.stepDown()方法,下面的例子使用了 rs.stepDown()方法進行降級:
rs.stepDown()
等主節點降級爲從節點,另外一個成員成爲 PRIMARY 以後,能夠按照 「遷移複製集的一個成員」遷移這個降級了的節點.可使用 rs.status()來確認狀態的改變。
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");
問題說明:某線上千萬級別集合,爲優化業務,直接執行新建索引命令,致使整個庫被鎖,應用服務出現不可用。
解決方案:找出此操做進程,而且殺死。改成後臺新建索引,速度會很慢,可是不會影響業務,該索引只會在新建完成以後,纔會生效;
# 查詢運行時間超過200ms操做 db.currentOp({"active" : true,"secs_running" : { "$gt" : 2000 }}) ; # 殺死執行時間過長操做操做 db.killOp(opid) # 後臺新建索引 db.collectionNmae.ensureIndex({filedName:1}, {background:true});
問題說明:生產環境某臺機器啓動多個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"})
問題說明:在刪除大量數據(本人操做的數據量在2000萬+)的情景下,而且在生產環境中請求量較大,此時機器的cpu負載會顯得很高,甚至機器卡頓沒法操做,這樣的操做應該謹慎分批量操做;在刪除命令執行結束以後,發現磁盤的數據量大小並無改變。
解決方案:
針對一些特殊狀況,不能下線secondary節點的,能夠新增一個節點到副本集中,而後secondary就自動開始數據的同步了。總的來講,重同步的方法是比較好的,第一基本不會阻塞副本集的讀寫,第二消耗的時間相對前兩種比較短。
問題說明:此情景是在客戶請求較大的情景性,因爲部署MongoDB的機器包含一主一從,MongoDB使得IO100%,數據庫阻塞,出現大量慢查詢,進而致使機器負載極高,應用服務徹底不可用。
解決方案:在沒有機器及時擴容的情況下,首要任務即是減少機器的IO,在一臺機器出現一主一從,在大量數據寫入的狀況下,會互相搶佔IO資源。因而此時摒棄了MongoDB高可用的特色,摘掉了複製集當中的從節點,保證每臺機器只有一個節點能夠佔用磁盤資源。以後,機器負載立馬下來,服務變爲正常可用狀態,可是此時MongoDB沒法保證數據的完整性,一旦有主節點掛掉便會丟失數據。
此方案只是臨時方法,根本解決是能夠增長機器的內存、使用固態硬盤,或者採用增長分片集來減小單個機器的讀寫壓力。
# 進入主節點,執行移除成員的命令 rs.remove("127.0.0.1:20001"); # 注意:切勿直接關停實例
問題說明:生產環境中,某一集合的片鍵使用了與_id生成方式類似,含有時間序列的字段做爲升序片鍵,致使數據寫入時都在一個數據塊,隨着數據量增大,會形成數據遷移到前面的分區,形成系統資源的佔用,偶爾出現慢查詢。
解決方案:臨時方案設置數據遷移的窗口,放在在正常的時間區段,對業務形成影響。根本解決是更換片鍵。
# 鏈接mongos實例,執行如下命令 db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "23:00", stop : "4:00" } } }, true ); # 查看均衡窗口 sh.getBalancerWindow();
查詢優化:確認你的查詢是否充分利用到了索引,用explain命令查看一下查詢執行的狀況,添加必要的索引,避免掃表操做。
合理設計分片鍵:
經過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() 查看當前的監控日誌。
可能你的數據集很是大,可是這並不那麼重要,重要的是你的熱數據集有多大,你常常訪問的數據有多大(包括常常訪問的數據和全部索引數據)。使用MongoDB,你最好保證你的熱數據在你機器的內存大小之下,保證內存能容納全部熱數據;
MongoDB的數據文件是採用的預分配模式,而且在Replication裏面,Master和Replica Sets的非Arbiter節點都是會預先建立足夠的空文件用以存儲操做日誌。這些文件分配操做在一些文件系統上可能會很是慢,致使進程被Block。因此咱們應該選擇那些空間分配快速的文件系統。這裏的結論是儘可能不要用ext3,用ext4或xfs;
儘量讓主從節點分攤在不一樣的機器上,避免IO操做的與MongoDB在同一臺機器;
MongoDB具備高性能、易擴展、易上手等特色,在正確使用的狀況下,其自己性能仍是很是強悍,在一些關鍵點如片鍵的選擇、內存的大小和磁盤IO,每每是限制其性能的最大瓶頸。針對於片鍵,在業務系統初期,能夠先不對集合進行數據分片,由於分片鍵一旦肯定就沒法修改,後期可根據業務系統的狀況,認真篩選字段。
通常狀況下,不建議使用升序片鍵(是一種隨着時間穩定增加的字段,自增加的主鍵是升序鍵 ),由於這個會致使局部的熱讀熱寫,不能發揮分片集羣的真正實力。建議使用hash片鍵或者隨機分發的片鍵,這樣能夠保證數據的均勻分發在分片節點;針對於內存,建議內存的大小可以包含熱數據的大小加索引大小,保證內存能容納全部熱數據 。
針對於磁盤資源,MongoDB的高速讀寫是以磁盤的IO做爲基礎,爲了保證其性能,建議將主從節點以及高IO的應用分離,以保證IO資源儘量不存在搶佔。
原文連接:https://www.jianshu.com/p/f05...