MongoDB使用中的一些問題

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

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

restore數據到新DB時,不要去先建索引

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

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表示安全寫入到多少個副本集才返回。服務器

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

使用組合索引的時候,若是有兩組索引,在限量查詢的狀況下,可能跟常規的認識不一樣: 利用組合索引作的查詢,在不一樣數量級下會有不一樣性能: 組合索引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索引,在大多數應用上執行得很好。app

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

作find的時候,並不要求索引必定要在前面, 譬如:db.test集合中對R有索引 db.test.find({R:"AA", "H": "BB"}).limit(100) db.test.find({"H":"BB", "R" : "AA"}).limit(100) 這兩個查找性能同樣,它都會使用R索引。只有當R屬性不參與排序且R的位置不影響查詢結果的條件下,纔會知足索引的無順序行。分佈式

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

集羣分片應遵循「總體分散,局部遞增」的原則。「固定值+增量值」 兩字段作組合索引能夠有效的實現分佈式集羣中的分散多熱點寫入、讀取。ide

在單個MongoDB實例上,最高效的寫入是順序寫入,而MongoDB集羣則要求寫入能隨機,以便平均分散到多個MongoDB實例。因此最高效的寫入是有多個局部熱點:在多個MongoDB實例之間是分散寫入,在實例內部是順序寫入。 要實現這一點,咱們採用組合索引。函數

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

在查詢時,索引是否高效,要注意它的cardinality(cardinality越高表示該鍵可選擇的值越多),在組合索引中,讓cardinality高的放在前面。也就是說在建立組合索引的時候,應該把枚舉類型、布爾類型等低cardinality的屬性排在後面,避免給低cardinality屬性建立獨立索引。

index cardinality(索引散列程度),表示的是一個索引所對應到的值的多少,散列程度越低,則一個索引鍵對應的值越多,索引效果越差。在使用索引時,高散列程度的索引能夠更多的排除不符合條件的文檔,讓後續的比較在一個更小的集合中執行,這更高效。因此通常選擇高散列程度的鍵作索引,或者在組合索引中,把高散列程度的鍵放在前面。

非原地update,性能會不好

update文檔時,若是新文檔的空間佔用大於舊文檔加上它周圍padding的空間,那麼就會放棄原來的位置,把數據拷貝到新空間。

TTL索引

TTL表明"time to live",TTL是一種特殊的索引,能夠將集合中過時的數據刪除。使用expireAfterSeconds 選項建立索引便可。 推薦和限制:

  • 使用usePowerOf2Sizes標識能夠更有效的防止磁盤碎片的產生。 db.runCommand( {collMod: "products", usePowerOf2Sizes : true }) db.runCommand( {collMod: "products", usePowerOf2Sizes : false })
  • TTL索引必須創建在date類型的字段上,若是不是date類型將不會被刪除。
  • TTL索引不能創建在_id字段上
  • TTL索引不能是聯合索引,不然會報錯,不讓建
  • 若是date類型中包含一個數組,好比time:['date1','date2'],那麼TTL會按照一個最先的進行過濾。
  • TTL不能創建在固定集合上(capped collection),由於固定集合不能刪除數據。 使用方法: db.log.ensureIndex( { "createDate": 1 }, { expireAfterSeconds: 3600 } )

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

若是索引創建指定了過時時間,後續要update過時時間能夠這樣子:

db.runCommand({"collMod":"a", index:{keyPattern:{"_":-1}, expireAfterSeconds: 60}})。 注意,經過collMod能修改過時時間的前提是:這個索引有過時時間,若是這個索引以前沒有設置過時時間,那麼沒法update,只能刪了索引,重建索引並指定過時時間。

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

###  **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的。

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

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

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

###  **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。


###  **shard key的選擇跟update性能緊密關聯**
分佈式MongoDB,shard key的選擇跟update性能,甚至是update可用性有很大關係,須要注意。
1.在對文檔個別字段update時,若是query部分沒有帶上shard key,性能會不好,由於mongos須要把這條update語句派發給全部的shard 實例。
2.update 的upsert參數爲true時,query部分必須帶上 shard key,不然語句執行出錯,例子:
>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下執行才能看到錯誤信息。

###  **經過repairDatabase提升性能**
從db.stats()中能夠看到幾個跟碎片相關的關鍵字段,
>- dataSize,表示數據的大小,它包含了padding的空間;
>- storageSize,表示這些數據存儲佔用的空間,包含了dataSize和被刪除數據所佔空間,

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


###  **索引字段的長度不能大於1024字節**
索引字段的長度不能大於1024字節,不然shell下會有插入錯誤提示:
>"errmsg" : "insertDocument :: caused by :: 17280 Btree::insert: key too large to index」。

###  **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。

###  **sort()方法的size限制**
當我對一個沒有建索引的字段作find,而後作sort的時候,可能觸發sort的size不超過32MB限制,例如:
>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
}
    
    有兩種解決方法:
解決方法一:對須要排序的字段建索引 db.stotal.ensureIndex({'type': -1})
解決方法二:修改默認配置,把sort時能夠用的內存設置大點:
>db.adminCommand({setParameter:1, internalQueryExecMaxBlockingSortBytes:335544320})

    這兩種解決方法各有利弊:(1)增長了索引會致使數據寫入變慢,存儲佔用變多;(2)不建索引修改默認配置,會致使sort的時候佔用更多的內存。
相關文章
相關標籤/搜索