本文完成時(shí)MongoDB的最新版本為MongoDB 2.6
1、count統(tǒng)計(jì)結(jié)果錯(cuò)誤
這是由于分布式集群正在遷移數(shù)據(jù),它導(dǎo)致count結(jié)果值錯(cuò)誤,需要使用aggregate pipeline來(lái)得到正確統(tǒng)計(jì)結(jié)果,例如:
db.collection.aggregate([{$group: {_id: null, count: {$sum: 1}}}])
引用:“On a sharded cluster, count can result in an inaccurate count if orphaned documents exist or if a chunk migration is in progress.”
參考:http://docs.mongodb.org/manual/reference/command/count/
2、從shell中更新/寫(xiě)入到文檔的數(shù)字,會(huì)變?yōu)閒loat類(lèi)型
引用:“shell中的數(shù)字都被MongoDB當(dāng)作是雙精度數(shù)。這意味著如果你從數(shù)據(jù)庫(kù)中獲得的是一個(gè)32位整數(shù),修改文檔后,將文檔存回?cái)?shù)據(jù)庫(kù)的時(shí)候,這個(gè)整數(shù)也就被換成了浮點(diǎn)數(shù),即便保持這個(gè)整數(shù)原封不動(dòng)也會(huì)這樣的?!?/p>
參考:《MongoDB權(quán)威指南》第一版
3、restore數(shù)據(jù)到新DB時(shí),不要去先建索引
把bson數(shù)據(jù)文件restore到另一個(gè)DB時(shí),需要注意:不能先創(chuàng)建索引再restore數(shù)據(jù),否則性能極差,mongorestore工具默認(rèn)會(huì)在restore完數(shù)據(jù)時(shí),根據(jù)dump出來(lái)的index信息創(chuàng)建索引,無(wú)須自己創(chuàng)建,如果是要更換索引,也應(yīng)該在數(shù)據(jù)入庫(kù)完之后再創(chuàng)建。
4、DB中的namespace數(shù)量太多導(dǎo)致無(wú)法創(chuàng)建新的collection
錯(cuò)誤提示:error: hashtable namespace index max chain reached:1335,如何解決呢?
這是DB中的collection個(gè)數(shù)太多導(dǎo)致,在實(shí)踐中以每個(gè)collection 8KB計(jì)算(跟官方文檔里說(shuō)的不同,可能跟index有關(guān)系),256MB可以支持36000個(gè)collection。db.system.namespaces.count() 命令可以統(tǒng)計(jì)當(dāng)前DB內(nèi)的collection數(shù)目,DB可支持collection數(shù)量是由于nssize參數(shù)指定的,它指定了dbname.ns磁盤(pán)文件的大小,也就指定了DB可支持的最大collection數(shù)目,ns為namespace縮寫(xiě)。默認(rèn)nssize為16MB。
如果重啟MongoD并修改了nssize參數(shù),這新nssize只會(huì)對(duì)新加入的DB生效,對(duì)以前已經(jīng)存在的DB不生效,如果你想對(duì)已經(jīng)存在的DB采用新的nssize,必須在加大nssize重啟之后新建DB,然后把舊DB的collection 復(fù)制到新DB中。
namespace限制相關(guān)文檔:http://docs.mongodb.org/manual/reference/limits/#Number-of-Namespaces
5、moveChunk因舊數(shù)據(jù)未刪除而失敗
錯(cuò)誤日志:”moveChunk failed to engage TO-shard in the data transfer: can't accept new chunks because there are still 1 deletes from previous migration“。
意思是說(shuō),當(dāng)前正要去接受新chunk 的shard正在刪除上一次數(shù)據(jù)遷移出的數(shù)據(jù),不能接受新Chunk,于是本次遷移失敗。這種log里顯示的是warning,但有時(shí)候會(huì)發(fā)現(xiàn)shard的刪除持續(xù)了十幾天都沒(méi)完成,查看日志,可以發(fā)現(xiàn)同一個(gè)chunk的刪除在不斷重復(fù)執(zhí)行,重啟所有無(wú)法接受新chunk的shard可以解決這個(gè)問(wèn)題。
參考:
http://stackoverflow.com/questions/26640861/movechunk-failed-to-engage-to-shard-in-the-data-transfer-cant-accept-new-chunk
如果采用了balancer自動(dòng)均衡,那么可以加上_waitForDelete參數(shù),如:
{ "_id" : "balancer", "activeWindow" : { "start" : "12:00", "stop" : "19:30" }, "stopped" : false, "_waitForDelete" : true }
,這樣就不會(huì)因delete堆積而導(dǎo)致后續(xù)migrate失敗,當(dāng)然,需要考慮到這里的阻塞是否會(huì)影響到程序正常運(yùn)轉(zhuǎn),在實(shí)踐中慎重采用使用waitForDelete,因?yàn)榘l(fā)現(xiàn)加上它之后遷移性能非常差,可能出現(xiàn)卡住十幾個(gè)小時(shí)的情況,外界拿住了被遷移chunk的游標(biāo)句柄,這時(shí)候刪除不能執(zhí)行,阻塞了后續(xù)其它遷移操作。
游標(biāo)被打開(kāi)而導(dǎo)致被遷移數(shù)據(jù)無(wú)法及時(shí)刪除時(shí)的日志:
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 ]
這可能會(huì)卡住幾十小時(shí),甚至一直卡住,影響后續(xù)的moveChunk操作,導(dǎo)致數(shù)據(jù)不均衡。
解決方法還是:重啟。
6、bson size不能超過(guò)16MB的限制
單個(gè)文檔的BSON size不能超過(guò)16MB。find查詢有時(shí)會(huì)遇到16MB的限制,譬如使用$in 查詢的時(shí)候,in中的數(shù)組元素不能太多。對(duì)一些特殊的數(shù)據(jù)源做MapReduce,MapReduce中間會(huì)將數(shù)據(jù)組合為“KEY:[VALUE1、VALUE2]”這樣的格式,當(dāng)value特別多的時(shí)候,也可能會(huì)遇上16MB的限制。 限制無(wú)處不在,需要注意,”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.
7、批量插入
批量插入可以減少數(shù)據(jù)往服務(wù)器的提交次數(shù),提高性能,一般批量提交的BSON size不超過(guò)48MB,如果超過(guò)了,驅(qū)動(dòng)程序自動(dòng)修改為往mongos的多次提交。
8、安全寫(xiě)入介紹及其沿革
關(guān)鍵字:acknowledge、write concern。
在2012年11月之前,MongoDB驅(qū)動(dòng)、shell客戶端默認(rèn)是不安全寫(xiě)入,也就是fire-and-forget,動(dòng)作發(fā)出之后,不關(guān)心是否真的寫(xiě)入成功,如果這時(shí)候出現(xiàn)了_id重復(fù)、非UTF8字符等異常,客戶端不會(huì)知道。在2012年11月之后,默認(rèn)為安全寫(xiě)入,安全級(jí)別相當(dāng)于參數(shù)w=1,客戶端可以知道寫(xiě)入操作是否成功。如果代碼使用Mongo或者Collection來(lái)連接數(shù)據(jù)庫(kù),則說(shuō)明它是默認(rèn)不安全寫(xiě)入的legacy代碼,安全寫(xiě)入已經(jīng)把連接數(shù)據(jù)庫(kù)修改為MongoClient接口。
安全寫(xiě)入可以分為三個(gè)級(jí)別,
第一級(jí)是默認(rèn)的安全寫(xiě)入,確認(rèn)數(shù)據(jù)寫(xiě)入到內(nèi)存中就返回(w=N屬于這一級(jí));
第二級(jí)是Journal save,數(shù)據(jù)在寫(xiě)入到DB磁盤(pán)文件之前,MongoDB會(huì)先把操作寫(xiě)入到Journal文件,這一級(jí)指的是確認(rèn)寫(xiě)入了Journal文件就返回;
第三級(jí)是fysnc,所有數(shù)據(jù)刷寫(xiě)到到DB磁盤(pán)文件才返回。
一般第一級(jí)就足夠了,第二級(jí)是為了保證在機(jī)器異常斷電的情況下也不會(huì)丟失數(shù)據(jù)。安全寫(xiě)入要付出性能的代碼:不安全寫(xiě)入的性能大概是默認(rèn)安全寫(xiě)入的3倍。使用fync參數(shù)則性能更差,一般不使用。
如果是副本集(replica set),其w=N參數(shù),N表示安全寫(xiě)入到多少個(gè)副本集才返回。
參考:
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/
9、善用索引——可能跟你以為的不一樣
使用組合索引的時(shí)候,如果有兩組索引,在限量查詢的情況下,可能跟常規(guī)的認(rèn)識(shí)不同:
利用組合索引做的查詢,在不同數(shù)量級(jí)下會(huì)有不同性能:
組合索引A: {"age": 1, "username": 1}
組合索引B: {"username": 1, "age": 1}
全量查詢: db.user.find({"age": {"$gte": 21, "$lte": 30}}).sort({"username" :1}),使用索引A的性能優(yōu)于索引B。
限量查詢: db.user.find({"age": {"$gte": 21, "$lte": 30}}).sort({"username": 1}).limit(1000),使用索引B的性能優(yōu)于索引A。
這兩個(gè)查詢?cè)谑褂盟饕鼳的時(shí)候,是先根據(jù)age索引找到符合age的數(shù)據(jù),然后再對(duì)這些結(jié)果做排序。使用索引B的時(shí)候,是遍歷name,對(duì)應(yīng)的數(shù)據(jù)判斷age,然后得到的結(jié)果是name有序的。
優(yōu)先使用sort key索引,在大多數(shù)應(yīng)用上執(zhí)行得很好。
參考:《MongoDB——The Definitive Guide 2nd Edition》page89
10、查詢時(shí)索引位置的無(wú)順序性
做find的時(shí)候,并不要求索引一定要在前面,
譬如:
db.test集合中對(duì)R有索引
db.test.find({R:"AA", "H": "BB"}).limit(100).explain()
db.test.find({"H":"BB", "R" : "AA"}).limit(100).explain()
這兩個(gè)查找性能一樣,它都會(huì)使用R索引。
11、使用組合索引做shard key可以大幅度提高集群性能
“固定值+增量值” 兩字段做組合索引可以有效的實(shí)現(xiàn)分布式集群中的分散多熱點(diǎn)寫(xiě)入、讀取。以下為讀書(shū)筆記:
在單個(gè)MongoDB實(shí)例上,最高效的寫(xiě)入是順序?qū)懭?,而MongoDB集群則要求寫(xiě)入能隨機(jī),以便平均分散到多個(gè)MongoDB實(shí)例。所以最高效的寫(xiě)入是有多個(gè)局部熱點(diǎn):在多個(gè)MongoDB實(shí)例之間是分散寫(xiě)入,在實(shí)例內(nèi)部是順序?qū)懭搿?要實(shí)現(xiàn)這一點(diǎn),我們采用組合索引。
例如:shardkey的第一部分是很粗糙的,可選集很少的字段,索引的第二部分是遞增字段,當(dāng)數(shù)據(jù)增加到一定程度時(shí),會(huì)出現(xiàn)很多第一部分相同第二部分不同的chunk,數(shù)據(jù)只會(huì)在最后一個(gè)chunk里寫(xiě)入數(shù)據(jù),當(dāng)?shù)谝徊糠植煌腸hunk分散在多個(gè)shard上,就實(shí)現(xiàn)了多熱點(diǎn)的寫(xiě)入。如果在一個(gè)shard上,不止一個(gè)chunk可以寫(xiě)入數(shù)據(jù),那也就是說(shuō)不止一個(gè)熱點(diǎn),當(dāng)熱點(diǎn)非常多的時(shí)候,也就等同于無(wú)熱點(diǎn)的隨機(jī)寫(xiě)入。當(dāng)一個(gè)chunk分裂之后,只能有一個(gè)成為熱點(diǎn),另一個(gè)不能再被寫(xiě)入,否則就會(huì)產(chǎn)生兩個(gè)熱點(diǎn),不再寫(xiě)入的chunk也就是死掉了,后續(xù)只會(huì)對(duì)它有讀操作。
最典型的應(yīng)用是具有日期屬性的日志處理,shard key選擇“日期+用戶ID”組合,保證了數(shù)據(jù)寫(xiě)入時(shí)的局部熱點(diǎn)(一個(gè)shard上只有少數(shù)幾個(gè)chunk被寫(xiě)入,避免隨機(jī)IO)和全局分散(所有的shard上都有寫(xiě)入數(shù)據(jù),充分利用磁盤(pán)IO)。
我在實(shí)踐中除了書(shū)中講到的組合鍵方式外,還加上了預(yù)分片策略,避免了早期數(shù)據(jù)增長(zhǎng)過(guò)程中的分片和數(shù)據(jù)遷移。另外還盡可能的制造能利用局部性原理的數(shù)據(jù)寫(xiě)入,例如在數(shù)據(jù)寫(xiě)入之前先對(duì)數(shù)據(jù)排序,有大約30%左右的update性能提升。
預(yù)分片是這樣子做的:根據(jù)組合shardkey信息先分裂好chunk,把這些空chunk移動(dòng)到各個(gè)shard上,避免了后續(xù)自動(dòng)分裂引起的數(shù)據(jù)遷移。
good case:
環(huán)境:一臺(tái)機(jī)器、7分片、MongoDB2.6版本、shard key選擇“日期+用戶ID組合”,
數(shù)據(jù):寫(xiě)入使用批量插入,對(duì)10億條日志級(jí)分片集群的寫(xiě)入,寫(xiě)入1000W條日志只需要35分鐘,每條日志約0.11K。
bad case:
環(huán)境:3臺(tái)機(jī)器、18分片、MongoDB2.6版本、shard key選擇 _id的hashid
數(shù)據(jù):寫(xiě)入采用批量插入,對(duì)3億條日志級(jí)分片集群的寫(xiě)入,寫(xiě)入300W條日志耗時(shí)35分鐘,每條日志約0.11K。
從對(duì)比可以看到,在數(shù)據(jù)量比較大的情況下選擇組合索引做shard key性能明顯優(yōu)于選擇hashid。
我在實(shí)際應(yīng)用中還遇到選擇hashid的更極端情況:對(duì)3條機(jī)器18分片3億條日志集群每天寫(xiě)入300W條日志,耗時(shí)170分鐘,每條日志約4K。每次寫(xiě)入數(shù)據(jù)時(shí),所有分片磁盤(pán)IO使用率都達(dá)到100%。
附創(chuàng)建組合索引和使用組合索引做shard key 的 mongo shell代碼:
xx> db.cswuyg.ensureIndex({'code':1, 'insert_time':1})
xx> use admin
admin> db.runCommand({"enablesharding":"xx"})
admin> db.runCommand({"shardcollection":"xx.cswuyg","key":{'code':1, 'insert_time':1}})
參考:《MongoDB——The Definitive Guide 2nd Edition》 page268
12、怎么建索引更能提高查詢性能?
在查詢時(shí),索引是否高效,要注意它的cardinality(cardinality越高表示該鍵可選擇的值越多),在組合索引中,讓cardinality高的放在前面。注意這里跟分布式環(huán)境選擇shard key的不同。以下為讀書(shū)筆記:
index cardinality(索引散列程度),表示的是一個(gè)索引所對(duì)應(yīng)到的值的多少,散列程度越低,則一個(gè)索引對(duì)應(yīng)的值越多,索引效果越差:在使用索引時(shí),高散列程度的索引可以更多的排除不符合條件的文檔,讓后續(xù)的比較在一個(gè)更小的集合中執(zhí)行,這更高效。所以一般選擇高散列程度的鍵做索引,或者在組合索引中,把高散列程度的鍵放在前面。
參考:《MongoDB——The Definitive Guide 2nd Edition》 page98
13、非原地update,性能會(huì)很差
update文檔時(shí),如果新文檔的空間占用大于舊文檔加上它周?chē)鷓adding的空間,那么就會(huì)放棄原來(lái)的位置,把數(shù)據(jù)拷貝到新空間。
參考:《MongoDB——The Definitive Guide 2nd Edition》 page43
14、無(wú)法在索引建立之后再去增加索引的過(guò)期時(shí)間
如果索引建立指定了過(guò)期時(shí)間,后續(xù)要update過(guò)期時(shí)間可以這樣子:db.runCommand({"collMod":"a", index:{keyPattern:{"_":-1}, expireAfterSeconds: 60}})。
注意,通過(guò)collMod能修改過(guò)期時(shí)間的前提是:這個(gè)索引有過(guò)期時(shí)間,如果這個(gè)索引之前沒(méi)有設(shè)置過(guò)期時(shí)間,那么無(wú)法update,只能刪了索引,重建索引并指定過(guò)期時(shí)間。
參考:http://docs.mongodb.org/manual/tutorial/expire-data/
15、_id索引無(wú)法刪除
參考:《MongoDB——The Definitive Guide 2nd Edition》 page114
16、paddingFactor是什么?
它是存儲(chǔ)空間冗余系數(shù),1.0表示沒(méi)有冗余,1.5表示50%的冗余空間,有了冗余空間,可以讓后續(xù)引發(fā)size增加的操作更快(不會(huì)導(dǎo)致重新分配磁盤(pán)空間和文檔遷移),一般是在1到4之間??梢酝ㄟ^(guò)db.collection.stats()看到collection的該值“paddingFactor”。
該值是MongoDB自己處理的,使用者無(wú)法設(shè)置paddingFactor。我們可以在compact的時(shí)候?qū)σ呀?jīng)有的文檔指定該值,但這個(gè)paddingFactor值不影響后續(xù)新插入的文檔。
repairDatabase跟compact類(lèi)似,也能移除冗余減少存儲(chǔ)空間,但冗余空間少了會(huì)導(dǎo)致后續(xù)增加文檔size的update操作變慢。
雖然我們無(wú)法設(shè)置paddingFactor,但是可以使用usePowerOf2Sizes保證分配的空間是2的倍數(shù),這樣也可以起到作用(MongoDB2.6版本起默認(rèn)啟用usePowerOf2Size)。
或者手動(dòng)實(shí)現(xiàn)padding:在插入文檔的時(shí)候先用默認(rèn)字符占用一塊空間,等到真實(shí)數(shù)據(jù)寫(xiě)入時(shí),再u(mài)nset掉它。
參考:
http://docs.mongodb.org/v2.4/core/record-padding/
http://docs.mongodb.org/v2.4/faq/developers/#faq-developers-manual-padding
17、usePowerOf2Size是什么
這是為更有效的復(fù)用磁盤(pán)空間而設(shè)置的參數(shù):分配的磁盤(pán)空間是2的倍數(shù),如果超過(guò)了4MB,則是距離計(jì)算值最近的且大于它的完整MB數(shù)。
可以通過(guò)db.collections.stats()看到該值“userFlags”。
MongoDB2.6之后默認(rèn)開(kāi)啟usePowerOf2Size參數(shù)
使用后的效果可以看這里的PPT:http://www.slideshare.net/mongodb/use-powerof2sizes-27300759
18、aggregate pipeline 指定運(yùn)算完成輸出文檔跟MapReduce相比有不足
(基于MongoDB2.6版本)MapReduce可以指定輸出到特定的db.collection中,例如:out_put = bson.SON([("replace", "collection_name" ), ("db", "xx_db")])
aggregate pipeline只能指定collection名字,也就意味著數(shù)據(jù)只能寫(xiě)入到本db,同時(shí)結(jié)果不能寫(xiě)入到capped collection、shard collection中。
相比之下,aggregate pipeline限制是比較多的,如果我們需要把結(jié)果放到某個(gè)DB下,則需要再做一次遷移:
db.runCommand({renameCollection:"sourcedb.mycol",to:"targetdb.mycol"})
但是?。∩厦娴倪@條命令要求在admin下執(zhí)行,且只能遷移往同shard下的DB,且被遷移的collection不能是shard的。
附錯(cuò)誤碼信息:
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
19、殺掉MongoD進(jìn)程的幾種方式
1)進(jìn)入到MongoD的命令行模式執(zhí)行shutdown,
eg:
$ mongo --port 10001
> use admin
> db.shutdownServer()
2)1方式的簡(jiǎn)化:
eg:mongo admin --port 10001 --eval "db.shutdownServer()"
3)使用MongoD命令行關(guān)閉,需要指定db路徑:
mongod --dbpath ./data/db --shutdown
20、集群的shard key慎重采用hash
如果你的日志是有日期屬性的,那么shard key不要使用hash,否則刪除過(guò)期日志時(shí)無(wú)法成塊刪除;在更新日志的時(shí)候,也不能利用局部性原理,查找、更新、插入數(shù)據(jù)都會(huì)因此而變慢。一般來(lái)說(shuō),hash id應(yīng)付小數(shù)據(jù)量時(shí)壓力不大,但在數(shù)據(jù)量較大(熱數(shù)據(jù)大于可用內(nèi)存容量)時(shí),CRUD性能極差,且會(huì)放大碎片對(duì)性能的影響:數(shù)據(jù)非常分散,當(dāng)有過(guò)期日志被刪除后,這些刪除后的空間成為碎片,可能會(huì)因?yàn)榇疟P(pán)預(yù)讀策略被加載到內(nèi)存中。另外,采用hash shard key還會(huì)浪費(fèi)掉一個(gè)索引,浪費(fèi)不少空間。
21、副本數(shù)也不用太多
如果你的副本數(shù)量超過(guò)了12個(gè)(MongoDB3.0.0超過(guò)了50個(gè)),那么就要選擇使用 master-slave ,但這樣會(huì)失去故障自恢復(fù)功能,主節(jié)點(diǎn)故障時(shí),需要手動(dòng)去切換到無(wú)故障節(jié)點(diǎn)。
22、mongos的config server配置信息中不要使用localhost、127.0.0.1
啟動(dòng)mongos時(shí),config server的配置信息不得使用localhost、127.0.0.1,否則添加其它機(jī)器的shard時(shí),會(huì)出現(xiàn)錯(cuò)誤提示:
"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啟動(dòng)mongos,也需要重啟config server,否則會(huì)有錯(cuò)誤提示:
“could not verify config servers were active and reachable before write”
如果改完后面又出現(xiàn) “mongos specified a different config database string” 錯(cuò)誤,那么還需要重啟mongod,
修改了config server 幾乎是要全部實(shí)例重啟。另外,在配置replica set時(shí)也不得使用localhost、127.0.0.1。
參考:http://stackoverflow.com/questions/21226255/where-is-the-mongos-config-database-string-being-stored
23、shard key的選擇跟update性能緊密關(guān)聯(lián)
分布式MongoDB,shard key的選擇跟update性能,甚至是update可用性有很大關(guān)系,需要注意。
1、在對(duì)文檔個(gè)別字段update時(shí),如果query部分沒(méi)有帶上shard key,性能會(huì)很差,因?yàn)閙ongos需要把這條update語(yǔ)句派發(fā)給所有的shard 實(shí)例。
2、當(dāng)update 的upsert參數(shù)為true時(shí),query部分必須帶上 shard key,否則語(yǔ)句執(zhí)行出錯(cuò),例子:
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 }"
}
})
這是因?yàn)槿绻麤](méi)有shard key,mongos既不能在所有shard實(shí)例上執(zhí)行這條語(yǔ)句(可能會(huì)導(dǎo)致每個(gè)shard都插入數(shù)據(jù)),也無(wú)法選擇在某個(gè)shard上執(zhí)行這條語(yǔ)句,于是出錯(cuò)了。
另外,需要特別注意,如果使用pymongo引擎,它不會(huì)告訴你出錯(cuò)了,只是函數(shù)調(diào)用陷入不返回,在shell下執(zhí)行才能看到錯(cuò)誤信息。
附:
以下英文部分來(lái)自: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.
意思是說(shuō),upsert產(chǎn)生了新的_id,_id就是shard key,但是如果query里沒(méi)有shard key,它們不知道要到哪個(gè)shard上執(zhí)行這個(gè)命令,upsert產(chǎn)生的shard key可能并不是執(zhí)行這條命令的shard的。
另外,如果_id不是shard key我們的例子也是不能成功的,因?yàn)闆](méi)有shard key,這條upsert要在哪個(gè)shard上執(zhí)行呢?不能像普通update那樣給所有的shard去做,否則可能導(dǎo)致插入多條。
參考:
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
24、通過(guò)repairDatabase提高性能
從db.stats()中可以看到幾個(gè)跟碎片相關(guān)的關(guān)鍵字段,dataSize,表示數(shù)據(jù)的大小,它包含了padding的空間;storageSize,表示這些數(shù)據(jù)存儲(chǔ)占用的空間,包含了dataSize和被刪除數(shù)據(jù)所占空間,可以認(rèn)為storageSize/dataSize就是磁盤(pán)碎片比例,當(dāng)刪除、update文檔比較多后,它會(huì)變大,考慮做repairDatabase,以減少碎片讓數(shù)據(jù)更緊湊,在實(shí)踐中,這對(duì)提高CURD性能極其有用。repairDatabase時(shí)需要注意:它是把數(shù)據(jù)拷貝到新的地方,然后再做處理,所以repair之前在DB目錄所在磁盤(pán)需要預(yù)留一倍的空閑磁盤(pán)空間,如果你發(fā)現(xiàn)磁盤(pán)空間不足,可以停止服務(wù),然后增加一塊新磁盤(pán),再執(zhí)行實(shí)例級(jí)別的repair,并指定--repairpath為新磁盤(pán)路徑,eg:mongod --dbpath /path/to/corrupt/data --repair --repairpath /media/external-hd/data/db,實(shí)例的數(shù)據(jù)會(huì)拷貝到/media/external-hd/data/db上做處理。
參考:《MongoDB——The Definitive Guide 2nd Edition》page325
25、索引字段的長(zhǎng)度不能大于1024字節(jié)
索引字段的長(zhǎng)度不能大于1024字節(jié),否則shell下會(huì)有插入錯(cuò)誤提示:"errmsg" : "insertDocument :: caused by :: 17280 Btree::insert: key too large to index”。
使用pymongo的“continue_on_error”參數(shù),不會(huì)發(fā)出錯(cuò)誤提示,要注意。
參考:http://docs.mongodb.org/manual/reference/limits/#Index-Key-Limit
26、修改索引的expireAfterSeconds之后,負(fù)載均衡失敗
修改索引的expireAfterSeconds之后,負(fù)載均衡失敗,出現(xiàn)錯(cuò)誤提示“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
檢查發(fā)生moveChunk的兩個(gè)shard,并沒(méi)有發(fā)現(xiàn)不一致,懷疑存在緩存,重啟所有shard解決。
27、config DB無(wú)法寫(xiě)入
因config DB無(wú)法修改,只可讀,導(dǎo)致drop、enablesharding失敗:
config server 相關(guān)日志:2015-06-11T16:51:19.078+0800 [replmaster] local.oplog.$main Assertion failure isOk() src/mongo/db/storage/extent.h 80
mongos 相關(guān)日志: [LockPinger] warning: pinging failed for distributed lock pinger 'xxx:1234/xxx:1235:1433993544:1804289383'. : : caused by :: isOk()
這是同事遇到的問(wèn)題,不確定是什么操作引起的。重啟、configdb做repair均無(wú)法解決。
最后通過(guò)dump、restore解決:(1)把舊configdb dump出來(lái);(2)restore到新的configure server;(3)mongos采用新的configure server;(4)重啟全部mongod。
28、sort()方法的size限制
當(dāng)我對(duì)一個(gè)沒(méi)有建索引的字段做find,然后做sort的時(shí)候,可能觸發(fā)sort的size的32MB限制,例如:
cswuyg> db.stotal.find({}).sort({'type':-1})
Error: error: {
"$err" : "Executor error: Overflow sort stage buffered data usage of 33554493 bytes exceeds internal limit of 33554432 bytes",
"code" : 17144
}
有兩種解決方法:
解決方法一:對(duì)需要排序的字段建索引 db.stotal.ensureIndex({'type': -1})
解決方法二:修改默認(rèn)配置,把sort時(shí)可以用的內(nèi)存設(shè)置大點(diǎn):cswuyg> db.adminCommand({setParameter:1, internalQueryExecMaxBlockingSortBytes:335544320})
參考:http://askubuntu.com/questions/501937/how-to-increase-buffered-data-limit
這兩種解決方法各有利弊:(1)增加了索引會(huì)導(dǎo)致數(shù)據(jù)寫(xiě)入變慢,存儲(chǔ)占用變多;(2)不建索引修改默認(rèn)配置,會(huì)導(dǎo)致sort的時(shí)候占用更多的內(nèi)存。
您可能感興趣的文章:- Mongodb啟動(dòng)命令參數(shù)中文說(shuō)明
- Mongodb 啟動(dòng)命令mongod參數(shù)說(shuō)明(中文翻譯)
- 解析PHP中常見(jiàn)的mongodb查詢操作
- Mongodb常見(jiàn)錯(cuò)誤與解決方法小結(jié)(Mongodb中經(jīng)常出現(xiàn)的錯(cuò)誤)
- mongodb 常見(jiàn)問(wèn)題處理方法收集
- 關(guān)于Mongodb參數(shù)說(shuō)明與常見(jiàn)錯(cuò)誤處理的總結(jié)