mongodb的處理

轉自:http://www.cnblogs.com/cswuyg/p/4355948.htmlhtml

 

一、count統計結果錯誤java

這是因爲分佈式集羣正在遷移數據,它致使count結果值錯誤,須要使用aggregate pipeline來獲得正確統計結果,例如:node

db.collection.aggregate([{$group: {_id: null, count: {$sum: 1}}}])git

引用:「On a sharded cluster, count can result in an inaccurate count if orphaned documents exist or if a chunk migration is in progress.」github

參考:http://docs.mongodb.org/manual/reference/command/count/ mongodb

二、從shell中更新/寫入到文檔的數字,會變爲float類型shell

引用:「shell中的數字都被MongoDB看成是雙精度數。這意味着若是你從數據庫中得到的是一個32位整數,修改文檔後,將文檔存回數據庫的時候,這個整數也就被換成了浮點數,即使保持這個整數原封不動也會這樣的。」數據庫

參考:《MongoDB權威指南》初版數組

三、restore數據到新DB時,不要去先建索引緩存

把bson數據文件restore到另外一個DB時,須要注意:不能先建立索引再restore數據,不然性能極差,mongorestore工具默認會在restore完數據時,根據dump出來的index信息建立索引,無須本身建立,若是是要更換索引,也應該在數據入庫完以後再建立。

四、DB中的namespace數量太多致使沒法建立新的collection

錯誤提示:error: hashtable namespace index max chain reached:1335,如何解決呢?
這是DB中的collection個數太多致使,在實踐中以每一個collection 8KB計算(跟官方文檔裏說的不一樣,可能跟index有關係),256MB能夠支持36000個collection。db.system.namespaces.count() 命令能夠統計當前DB內的collection數目,DB可支持collection數量是因爲nssize參數指定的,它指定了dbname.ns磁盤文件的大小,也就指定了DB可支持的最大collection數目,ns爲namespace縮寫。默認nssize爲16MB。
若是重啓MongoD並修改了nssize參數,這新nssize只會對新加入的DB生效,對之前已經存在的DB不生效,若是你想對已經存在的DB採用新的nssize,必須在加大nssize重啓以後新建DB,而後把舊DB的collection 複製到新DB中。
namespace限制相關文檔:http://docs.mongodb.org/manual/reference/limits/#Number-of-Namespaces

五、moveChunk因舊數據未刪除而失敗

錯誤日誌:」moveChunk failed to engage TO-shard in the data transfer: can't accept new chunks because there are still 1 deletes from previous migration「。
意思是說,當前正要去接受新chunk 的shard正在刪除上一次數據遷移出的數據,不能接受新Chunk,因而本次遷移失敗。這種log裏顯示的是warning,但有時候會發現shard的刪除持續了十幾天都沒完成,查看日誌,能夠發現同一個chunk的刪除在不斷重複執行,重啓全部沒法接受新chunk的shard能夠解決這個問題。
參考:
http://stackoverflow.com/questions/26640861/movechunk-failed-to-engage-to-shard-in-the-data-transfer-cant-accept-new-chunk
若是採用了balancer自動均衡,那麼能夠加上_waitForDelete參數,如:
{ "_id" : "balancer", "activeWindow" : { "start" : "12:00", "stop" : "19:30" }, "stopped" : false, "_waitForDelete" : true }
,這樣就不會因delete堆積而致使後續migrate失敗,固然,須要考慮到這裏的阻塞是否會影響到程序正常運轉,在實踐中慎重採用使用waitForDelete,由於發現加上它以後遷移性能很是差,可能出現卡住十幾個小時的狀況,外界拿住了被遷移chunk的遊標句柄,這時候刪除不能執行,阻塞了後續其它遷移操做。
遊標被打開而致使被遷移數據沒法及時刪除時的日誌:
2015-03-07T10:21:20.118+0800 [RangeDeleter] rangeDeleter waiting for open cursors in: cswuyg_test.cswuyg_test, min: { _id: -6665031702664277348 }, max: { _id: -6651575076051867067 }, elapsedSecs: 6131244, cursors: [ 220477635588 ]
這可能會卡住幾十小時,甚至一直卡住,影響後續的moveChunk操做,致使數據不均衡。
解決方法仍是:重啓。

六、bson size不能超過16MB的限制

單個文檔的BSON size不能超過16MB。find查詢有時會遇到16MB的限制,譬如使用$in 查詢的時候,in中的數組元素不能太多。對一些特殊的數據源作MapReduce,MapReduce中間會將數據組合爲「KEY:[VALUE一、VALUE2]」這樣的格式,當value特別多的時候,也可能會趕上16MB的限制。 限制無處不在,須要注意,」The issue is that the 16MB document limit applies to everything - documents you store, documents MapReduce tries to generate, documents aggregation tries to return, etc.

七、批量插入

批量插入能夠減小數據往服務器的提交次數,提升性能,通常批量提交的BSON size不超過48MB,若是超過了,驅動程序自動修改成往mongos的屢次提交。

八、安全寫入介紹及其沿革

關鍵字:acknowledge、write concern。

在2012年11月以前,MongoDB驅動、shell客戶端默認是不安全寫入,也就是fire-and-forget,動做發出以後,不關心是否真的寫入成功,若是這時候出現了_id重複、非UTF8字符等異常,客戶端不會知道。在2012年11月以後,默認爲安全寫入,安全級別至關於參數w=1,客戶端能夠知道寫入操做是否成功。若是代碼使用Mongo或者Collection來鏈接數據庫,則說明它是默認不安全寫入的legacy代碼,安全寫入已經把鏈接數據庫修改成MongoClient接口。
安全寫入能夠分爲三個級別,
第一級是默認的安全寫入,確認數據寫入到內存中就返回(w=N屬於這一級);
第二級是Journal save,數據在寫入到DB磁盤文件以前,MongoDB會先把操做寫入到Journal文件,這一級指的是確認寫入了Journal文件就返回;
第三級是fysnc,全部數據刷寫到到DB磁盤文件才返回。
通常第一級就足夠了,第二級是爲了保證在機器異常斷電的狀況下也不會丟失數據。安全寫入要付出性能的代碼:不安全寫入的性能大概是默認安全寫入的3倍。使用fync參數則性能更差,通常不使用。
若是是副本集(replica set),其w=N參數,N表示安全寫入到多少個副本集才返回。
參考:
http://docs.mongodb.org/manual/release-notes/drivers-write-concern/
http://docs.mongodb.org/manual/core/write-concern/
http://blog.mongodirector.com/understanding-durability-write-safety-in-mongodb/
http://whyjava.wordpress.com/2011/12/08/how-mongodb-different-write-concern-values-affect-performance-on-a-single-node/

九、善用索引——可能跟你覺得的不同

使用組合索引的時候,若是有兩組索引,在限量查詢的狀況下,可能跟常規的認識不一樣:
利用組合索引作的查詢,在不一樣數量級下會有不一樣性能:
組合索引A: {"age": 1, "username": 1}
組合索引B: {"username": 1, "age": 1}
全量查詢: db.user.find({"age": {"$gte": 21, "$lte": 30}}).sort({"username" :1}),使用索引A的性能優於索引B。
限量查詢: db.user.find({"age": {"$gte": 21, "$lte": 30}}).sort({"username": 1}).limit(1000),使用索引B的性能優於索引A。
這兩個查詢在使用索引A的時候,是先根據age索引找到符合age的數據,而後再對這些結果作排序。使用索引B的時候,是遍歷name,對應的數據判斷age,而後獲得的結果是name有序的。
優先使用sort key索引,在大多數應用上執行得很好。
參考:《MongoDB——The Definitive Guide 2nd Edition》page89

十、查詢時索引位置的無順序性

作find的時候,並不要求索引必定要在前面,
譬如:
db.test集合中對R有索引
db.test.find({R:"AA", "H": "BB"}).limit(100).explain()
db.test.find({"H":"BB", "R" : "AA"}).limit(100).explain()
這兩個查找性能同樣,它都會使用R索引。

十一、使用組合索引作shard key能夠大幅度提升集羣性能

「固定值+增量值」 兩字段作組合索引能夠有效的實現分佈式集羣中的分散多熱點寫入、讀取。如下爲讀書筆記:
在單個MongoDB實例上,最高效的寫入是順序寫入,而MongoDB集羣則要求寫入能隨機,以便平均分散到多個MongoDB實例。因此最高效的寫入是有多個局部熱點:在多個MongoDB實例之間是分散寫入,在實例內部是順序寫入。 要實現這一點,咱們採用組合索引。
例如:shardkey的第一部分是很粗糙的,可選集不多的字段,索引的第二部分是遞增字段,當數據增長到必定程度時,會出現不少第一部分相同第二部分不一樣的chunk,數據只會在最後一個chunk裏寫入數據,當第一部分不一樣的chunk分散在多個shard上,就實現了多熱點的寫入。若是在一個shard上,不止一個chunk能夠寫入數據,那也就是說不止一個熱點,當熱點很是多的時候,也就等同於無熱點的隨機寫入。當一個chunk分裂以後,只能有一個成爲熱點,另外一個不能再被寫入,不然就會產生兩個熱點,再也不寫入的chunk也就是死掉了,後續只會對它有讀操做。
我在實踐中除了書中講到的組合鍵方式外,還加上了預分片策略,避免了早期數據增加過程當中的分片和數據遷移。另外還儘量的製造能利用局部性原理的數據寫入,例如在數據寫入以前先對數據排序,有大約30%左右的update性能提高。
預分片是這樣子作的:根據組合shardkey信息先分裂好chunk,把這些空chunk移動到各個shard上,避免了後續自動分裂引發的數據遷移。
參考:《MongoDB——The Definitive Guide 2nd Edition》 page268

十二、怎麼建索引更能提升查詢性能?

在查詢時,索引是否高效,要注意它的cardinality(cardinality越高表示該鍵可選擇的值越多),在組合索引中,讓cardinality高的放在前面。注意這裏跟分佈式環境選擇shard key的不一樣。如下爲讀書筆記:
index cardinality(索引散列程度),表示的是一個索引所對應到的值的多少,散列程度越低,則一個索引對應的值越多,索引效果越差:在使用索引時,高散列程度的索引能夠更多的排除不符合條件的文檔,讓後續的比較在一個更小的集合中執行,這更高效。因此通常選擇高散列程度的鍵作索引,或者在組合索引中,把高散列程度的鍵放在前面。
參考:《MongoDB——The Definitive Guide 2nd Edition》 page98

1三、非原地update,性能會不好

update文檔時,若是新文檔的空間佔用大於舊文檔加上它周圍padding的空間,那麼就會放棄原來的位置,把數據拷貝到新空間。
參考:《MongoDB——The Definitive Guide 2nd Edition》 page43

1四、沒法在索引創建以後再去增長索引的過時時間

若是索引創建指定了過時時間,後續要update過時時間能夠這樣子:db.runCommand({"collMod":"a", index:{keyPattern:{"_":-1}, expireAfterSeconds: 60}})。

注意,經過collMod能修改過時時間的前提是:這個索引有過時時間,若是這個索引以前沒有設置過時時間,那麼沒法update,只能刪了索引,重建索引並指定過時時間。
參考:http://docs.mongodb.org/manual/tutorial/expire-data/

1五、_id索引沒法刪除

參考:《MongoDB——The Definitive Guide 2nd Edition》 page114

1六、paddingFactor是什麼?

它是存儲空間冗餘係數,1.0表示沒有冗餘,1.5表示50%的冗餘空間,有了冗餘空間,可讓後續引起size增長的操做更快(不會致使從新分配磁盤空間和文檔遷移),通常是在1到4之間。能夠經過db.collection.stats()看到collection的該值「paddingFactor」。
該值是MongoDB本身處理的,使用者沒法設置paddingFactor。咱們能夠在compact的時候對已經有的文檔指定該值,但這個paddingFactor值不影響後續新插入的文檔。
repairDatabase跟compact相似,也能移除冗餘減小存儲空間,但冗餘空間少了會致使後續增長文檔size的update操做變慢。
雖然咱們沒法設置paddingFactor,可是可使用usePowerOf2Sizes保證分配的空間是2的倍數,這樣也能夠起到做用(MongoDB2.6版本起默認啓用usePowerOf2Size)。
或者手動實現padding:在插入文檔的時候先用默認字符佔用一塊空間,等到真實數據寫入時,再unset掉它。

參考:
http://docs.mongodb.org/v2.4/core/record-padding/
http://docs.mongodb.org/v2.4/faq/developers/#faq-developers-manual-padding

1七、usePowerOf2Size是什麼

這是爲更有效的複用磁盤空間而設置的參數:分配的磁盤空間是2的倍數,若是超過了4MB,則是距離計算值最近的且大於它的完整MB數。
能夠經過db.collections.stats()看到該值「userFlags」。
MongoDB2.6以後默認開啓usePowerOf2Size參數
使用後的效果能夠看這裏的PPT:http://www.slideshare.net/mongodb/use-powerof2sizes-27300759

1八、aggregate pipeline 指定運算完成輸出文檔跟MapReduce相比有不足

(基於MongoDB2.6版本)MapReduce能夠指定輸出到特定的db.collection中,例如:out_put = bson.SON([("replace", "collection_name" ), ("db", "xx_db")])
aggregate pipeline只能指定collection名字,也就意味着數據只能寫入到本db,同時結果不能寫入到capped collection、shard collection中。
相比之下,aggregate pipeline限制是比較多的,若是咱們須要把結果放到某個DB下,則須要再作一次遷移:
db.runCommand({renameCollection:"sourcedb.mycol",to:"targetdb.mycol"})
可是!!上面的這條命令要求在admin下執行,且只能遷移往同shard下的DB,且被遷移的collection不能是shard的。
附錯誤碼信息:
https://github.com/mongodb/mongo/blob/master/src/mongo/s/commands_public.cpp#L778
uassert(13140, "Don't recognize source or target DB", confFrom && confTo);
uassert(13138, "You can't rename a sharded collection", !confFrom->isSharded(fullnsFrom));
uassert(13139, "You can't rename to a sharded collection", !confTo->isSharded(fullnsTo));
uassert(13137, "Source and destination collections must be on same shard", shardFrom == shardTo);
參考:http://docs.mongodb.org/manual/reference/method/db.collection.mapReduce/#mapreduce-out-mtd

1九、殺掉MongoD進程的幾種方式

1)進入到MongoD的命令行模式執行shutdown,
eg: 
$ mongo --port 10001
> use admin
> db.shutdownServer()
2)1方式的簡化:
eg:mongo admin --port 10001 --eval "db.shutdownServer()"
3)使用MongoD命令行關閉,須要指定db路徑:
mongod --dbpath ./data/db --shutdown

20、集羣的shard key慎重採用hash

若是你的日誌是有日期屬性的,那麼shard key不要使用hash,不然刪除過時日誌時沒法成塊刪除;在更新日誌的時候,也不能利用局部性原理,查找、更新、插入數據都會所以而變慢。通常來講,hash id應付小數據量時壓力不大,但在數據量較大(熱數據大於可用內存容量)時,CRUD性能極差,且會放大碎片對性能的影響:數據很是分散,當有過時日誌被刪除後,這些刪除後的空間成爲碎片,可能會由於磁盤預讀策略被加載到內存中。另外,採用hash shard key還會浪費掉一個索引,浪費很多空間。

2一、副本數也不用太多

若是你的副本數量超過了12個(MongoDB3.0.0超過了50個),那麼就要選擇使用 master-slave ,但這樣會失去故障自恢復功能,主節點故障時,須要手動去切換到無端障節點。

2二、mongos的config server配置信息中不要使用localhost、127.0.0.1

啓動mongos時,config server的配置信息不得使用localhost、127.0.0.1,不然添加其它機器的shard時,會出現錯誤提示:
"can’t use localhost as a shard since all shards need to communicate. either use all shards and configdbs in localhost or all in actual IPs host: xxxxx isLocalHost"

以新的config server啓動mongos,也須要重啓config server,不然會有錯誤提示:
「could not verify config servers were active and reachable before write」

若是改完後面又出現 「mongos specified a different config database string」  錯誤,那麼還須要重啓mongod,

修改了config server 幾乎是要所有實例重啓。另外,在配置replica set時也不得使用localhost、127.0.0.1。
參考:http://stackoverflow.com/questions/21226255/where-is-the-mongos-config-database-string-being-stored

2三、shard key的選擇跟update性能緊密關聯

分佈式MongoDB,shard key的選擇跟update性能,甚至是update可用性有很大關係,須要注意。
一、在對文檔個別字段update時,若是query部分沒有帶上shard key,性能會不好,由於mongos須要把這條update語句派發給全部的shard 實例。
二、當update 的upsert參數爲true時,query部分必須帶上 shard key,不然語句執行出錯,例子:
mongos> db.test.update({"_id":".7269993106A92327A89ABCD70D46AD5"}, {"$set":{"P": "aaa"}, "$setOnInsert":{"TEST":"a"}}, true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 61,
"errmsg" : "upsert { q: { _id: \".7269993106A92327A89ABCD70D46AD5\" }, u: { $set: { P: "aaa" }, $setOnInsert: { TEST: \"a\" } }, multi: false, upsert: true } does not contain shard key for pattern { _: 1.0, B: 1.0 }"
}
})
這是由於若是沒有shard key,mongos既不能在全部shard實例上執行這條語句(可能會致使每一個shard都插入數據),也沒法選擇在某個shard上執行這條語句,因而出錯了。
另外,須要特別注意,若是使用pymongo引擎,它不會告訴你出錯了,只是函數調用陷入不返回,在shell下執行才能看到錯誤信息。

附:
如下英文部分來自:https://jira.mongodb.org/browse/SERVER-13010
It's actually not clear to me that this is something we can support - problem is this:
> db.coll.update({ _id : 1 }, { }, true);
> db.coll.find()
{ "_id" : ObjectId("53176700a2bc4d46c176f14a") }
Upserts generate new _ids in response to this operation, and therefore we can't actually target this correctly in a sharded environment. The shard on which we need to perform the query may not be the shard on which the new _id is placed.
意思是說,upsert產生了新的_id,_id就是shard key,可是若是query裏沒有shard key,它們不知道要到哪一個shard上執行這個命令,upsert產生的shard key可能並非執行這條命令的shard的。
另外,若是_id不是shard key咱們的例子也是不能成功的,由於沒有shard key,這條upsert要在哪一個shard上執行呢?不能像普通update那樣給全部的shard去作,不然可能致使插入多條。
參考:
https://jira.mongodb.org/browse/SERVER-13010
http://docs.mongodb.org/manual/core/sharding-shard-key/
http://stackoverflow.com/questions/17190246/which-of-the-following-statements-are-true-about-choosing-and-using-a-shard-key

2四、經過repairDatabase提升性能

從db.stats()中能夠看到幾個跟碎片相關的關鍵字段,dataSize,表示數據的大小,它包含了padding的空間;storageSize,表示這些數據存儲佔用的空間,包含了dataSize和被刪除數據所佔空間,能夠認爲storageSize/dataSize就是磁盤碎片比例,當刪除、update文檔比較多後,它會變大,考慮作repairDatabase,以減小碎片讓數據更緊湊,在實踐中,這對提升CURD性能極其有用。repairDatabase時須要注意:它是把數據拷貝到新的地方,而後再作處理,因此repair以前在DB目錄所在磁盤須要預留一倍的空閒磁盤空間,若是你發現磁盤空間不足,能夠中止服務,而後增長一塊新磁盤,再執行實例級別的repair,並指定--repairpath爲新磁盤路徑,eg:mongod --dbpath /path/to/corrupt/data --repair --repairpath /media/external-hd/data/db,實例的數據會拷貝到/media/external-hd/data/db上作處理。

參考:《MongoDB——The Definitive Guide 2nd Edition》page325

2五、索引字段的長度不能大於1024字節

索引字段的長度不能大於1024字節,不然shell下會有插入錯誤提示:"errmsg" : "insertDocument :: caused by :: 17280 Btree::insert: key too large to index」。
使用pymongo的「continue_on_error」參數,不會發出錯誤提示,要注意。

2六、修改索引的expireAfterSeconds以後,負載均衡失敗

修改索引的expireAfterSeconds以後,負載均衡失敗,出現錯誤提示「2015-06-05T09:59:49.056+0800 [migrateThread] warning: failed to create index before migrating data.  idx: { v: 1, key: { _: -1 }, name: "__-1", ns: "cswuyg_test.cswuyg_test", expireAfterSeconds: 5227200 } error: IndexOptionsConflict Index with name: __-1 already exists with different options
檢查發生moveChunk的兩個shard,並無發現不一致,懷疑存在緩存,重啓全部shard解決。

2七、config DB沒法寫入

因config DB沒法修改,只可讀,致使drop、enablesharding失敗:
config server 相關日誌:2015-06-11T16:51:19.078+0800 [replmaster] local.oplog.$main Assertion failure isOk() src/mongo/db/storage/extent.h 80
mongos 相關日誌: [LockPinger] warning: pinging failed for distributed lock pinger 'xxx:1234/xxx:1235:1433993544:1804289383'. : : caused by :: isOk()
這是同事遇到的問題,不肯定是什麼操做引發的。重啓、configdb作repair均沒法解決。
最後經過dump、restore解決:(1)把舊configdb dump出來;(2)restore到新的configure server;(3)mongos採用新的configure server;(4)重啓所有mongod。
 
28,mongodb斷電
http://blog.csdn.net/hhh871030/article/details/7835724

Mongodb相信你們都比較熟悉了,將它註冊爲服務什麼的就不說了,網上處處都是。在公司用的過程當中,我發如今意外斷電,或者強制關機以後,啓動服務時候就會報錯,找了好久,試了不少種方法,才發現,它有個自帶的修復命令,將其執行如下就ok了

      Mongod --repair --dbpath=D:\Mongodb\data(即你設置服務的時候,用於放數據庫的位置)

注意repair 是2個 -

 如下列出參考的一些網址,有興趣的能夠學習使用,有任何錯誤,歡迎即時修正,獎勵棒棒糖一個(已經吃過的)

http://www.cnblogs.com/huangxincheng/category/355399.html 8天學通MongoDB
http://www.cnblogs.com/silentcross/category/306488.html  MongoDB權威指南

http://special.csdn.net/mongodb/index.html MongoDB開發應用實戰

 

http://www.cnblogs.com/lipan/category/273923.html MongoDB學習筆記

 

http://www.cnblogs.com/shanyou/archive/2011/05/20/2052354.html  MongoVue

相關文章
相關標籤/搜索