進行了一下Mongodb億級數據量的性能測試,分別測試以下幾個項目:mongodb
(全部插入都是單線程進行,全部讀取都是多線程進行)數據庫
1) 普通插入性能 (插入的數據每條大約在1KB左右)安全
2) 批量插入性能 (使用的是官方C#客戶端的InsertBatch),這個測的是批量插入性能能有多少提升服務器
3) 安全插入功能 (確保插入成功,使用的是SafeMode.True開關),這個測的是安全插入性能會差多少網絡
4) 查詢一個索引後的數字列,返回10條記錄(也就是10KB)的性能,這個測的是索引查詢的性能多線程
5) 查詢兩個索引後的數字列,返回10條記錄(每條記錄只返回20字節左右的2個小字段)的性能,這個測的是返回小數據量以及多一個查詢條件對性能的影響性能
6) 查詢一個索引後的數字列,按照另外一個索引的日期字段排序(索引創建的時候是倒序,排序也是倒序),而且Skip100條記錄後返回10條記錄的性能,這個測的是Skip和Order對性能的影響測試
7) 查詢100條記錄(也就是100KB)的性能(沒有排序,沒有條件),這個測的是大數據量的查詢結果對性能的影響大數據
8) 統計隨着測試的進行,總磁盤佔用,索引磁盤佔用以及數據磁盤佔用的數量編碼
而且每一種測試都使用單進程的Mongodb和同一臺服務器開三個Mongodb進程做爲Sharding(每個進程大概只能用7GB左右的內存)兩種方案
其實對於Sharding,雖然是一臺機器放3個進程,可是在查詢的時候每個並行進程查詢部分數據,再有運行於另一個機器的mongos來彙總數據,理論上來講在某些狀況下性能會有點提升
基於以上的種種假設,猜想某些狀況性能會降低,某些狀況性能會提升,那麼來看一下最後的測試結果怎麼樣?
備註:測試的存儲服務器是 E5620 @ 2.40GHz,24GB內存,CentOs操做系統,打壓機器是E5504 @ 2.0GHz,4GB內存,Windows Server 2003操做系統,二者千兆網卡直連。
從這個測試能夠看出,對於單進程的方式:
1) Mongodb的非安全插入方式,在一開始插入性能是很是高的,可是在達到了兩千萬條數據以後性能驟減,這個時候恰巧是服務器24G內存基本佔滿的時候 (隨着測試的進行mongodb不斷佔據內存,一直到操做系統的內存所有佔滿),也就是說Mongodb的內存映射方式,使得數據所有在內存中的時候速度 飛快,當部分數據須要換出到磁盤上以後,性能降低很厲害。(這個性能其實也不算太差,由於咱們對三個列的數據作了索引,即便在內存滿了以後每秒也能插入 2MB的數據,在一開始更是每秒插入25MB數據)。Foursquare其實也是把Mongodb看成帶持久化的內存數據庫使用的,只是在查不到達到內 存瓶頸的時候Sharding沒處理好。
2) 對於批量插入功能,實際上是一次提交一批數據,可是相比一次一條插入性能並無提升多少,一來是由於網絡帶寬已經成爲了瓶頸,二來我想寫鎖也會是一個緣由。
3) 對於安全插入功能,相對來講比較穩定,不會波動很大,我想多是由於安全插入是確保數據直接持久化到磁盤的,而不是插入內存就完事。
4) 對於一列條件的查詢,性能一直比較穩定,別小看,每秒能有8000-9000的查詢次數,每次返回10KB,至關於每秒查詢80MB數據,並且數據庫記錄是2億以後還能維持這個水平,性能驚人。
5) 對於二列條件返回小數據的查詢,整體上性能會比4)好一點,可能返回的數據量小對性能提升比較大,可是相對來講性能波動也厲害一點,可能多了一個條件就多了一個從磁盤換頁的機會。
6) 對於一列數據外加Sort和Skip的查詢,在數據量大了以後性能明顯就變差了(此時是索引數據量超過內存大小的時候,不知道是否有聯繫),我猜測是Skip比較消耗性能,不過和4)相比性能也不是差距特別大。
7) 對於返回大數據的查詢,一秒瓶頸也有800次左右,也就是80M數據,這就進一步說明了在有索引的狀況下,順序查詢和按條件搜索性能是相差無幾的,這個時候是IO和網絡的瓶頸。
8) 在整個過程當中索引佔的數據量已經佔到了總數據量的至關大比例,在達到1億4千萬數據量的時候,光索引就能夠佔據整個內存,此時查詢性能仍是很是高,插入性能也不算太差,mongodb的性能確實很牛。
那麼在來看看Sharding模式有什麼亮點:
1) 非安全插入和單進程的配置同樣,在內存滿了以後性能急劇降低。安全插入性能和單進程相比慢很多,可是很是穩定。
2) 對於一個條件和兩個條件的查詢,性能都比較穩定,但條件查詢性能至關於單進程的一半,可是在多條件下有的時候甚至會比單進程高一點。我想這多是某些時候 數據塊位於兩個Sharding,這樣Mongos會並行在兩個Sharding查詢,而後在把數據進行合併彙總,因爲查詢返回的數據量小,網絡不太可能 成爲瓶頸了,使得Sharding纔有出頭的機會。
3) 對於Order和Skip的查詢,Sharding方式的差距就出來了,我想主要性能損失可能在Order,由於咱們並無按照排序字段做爲Sharding的Key,使用的是_id做爲Key,這樣排序就比較難進行。
4) 對於返回大數據量的查詢,Sharding方式其實和單進程差距不是很大,我想數據的轉發多是一個性能損耗的緣由(雖然mongos位於打壓機本機,可是數據始終是轉手了一次)。
5) 對於磁盤空間的佔用,二者實際上是差很少的,其中的一些差距多是由於多個進程都會多分配一點空間,加起來有的時候會比單進程多佔用點磁盤(而那些佔用比單進程少的地方實際上是開始的編碼錯誤,把實際數據大小和磁盤文件佔用大小搞錯了)。
測試最後的各個Sharding分佈狀況以下:
{
"sharded" : true,
"ns" : "testdb.test",
"count" : 209766143,
"size" : 214800530672,
"avgObjSize" : 1024.0000011441311,
"storageSize" : 222462757776,
"nindexes" : 4,
"nchunks" : 823,
"shards" : {
"shard0000" : {
"ns" : "testdb.test",
"count" : 69474248,
"size" : 71141630032,
"avgObjSize" : 1024.0000011515058,
"storageSize" : 74154252592,
"numExtents" : 65,
"nindexes" : 4,
"lastExtentSize" : 2146426864,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 11294125824,
"indexSizes" : {
"_id_" : 2928157632,
"Number_1" : 2832745408,
"Number1_1" : 2833974208,
"Date_-1" : 2699248576
},
"ok" : 1
},
"shard0001" : {
"ns" : "testdb.test",
"count" : 70446092,
"size" : 72136798288,
"avgObjSize" : 1024.00000113562,
"storageSize" : 74154252592,
"numExtents" : 65,
"nindexes" : 4,
"lastExtentSize" : 2146426864,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 11394068224,
"indexSizes" : {
"_id_" : 2969355200,
"Number_1" : 2826453952,
"Number1_1" : 2828403648,
"Date_-1" : 2769855424
},
"ok" : 1
},
"shard0002" : {
"ns" : "testdb.test",
"count" : 69845803,
"size" : 71522102352,
"avgObjSize" : 1024.00000114538,
"storageSize" : 74154252592,
"numExtents" : 65,
"nindexes" : 4,
"lastExtentSize" : 2146426864,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 11300515584,
"indexSizes" : {
"_id_" : 2930942912,
"Number_1" : 2835243968,
"Number1_1" : 2835907520,
"Date_-1" : 2698421184
},
"ok" : 1
}
},
"ok" : 1
}
雖然在最後因爲時間的關係,沒有測到10億級別的數據量,可是經過這些數據已經能夠證實Mongodb的性能是多麼強勁了。另一個緣由是,在不少時候可能數據只達到千萬咱們就會對庫進行拆分,不會讓一個庫的索引很是龐大。在測試的過程當中還發現幾個問題須要值得注意:
1) 在數據量很大的狀況下,對服務進行重啓,那麼服務啓動的初始化階段,雖然能夠接受數據的查詢和修改,可是此時性能不好,由於mongodb會不斷把數據從磁盤換入內存,此時的IO壓力很是大。
2) 在數據量很大的狀況下,若是服務沒有正常關閉,那麼Mongodb啓動修復數據庫的時間很是可觀,在1.8中退出的-dur貌似能夠解決這個問題,據官方說對讀取沒影響,寫入速度會稍稍下降,有空我也會再進行下測試。
3) 在使用Sharding的時候,Mongodb時不時會對數據拆分搬遷,這個時候性能降低很厲害,雖然從測試圖中看不出(由於我每一次測試都會測試比較多 的迭代次數),可是我在實際觀察中能夠發現,在搬遷數據的時候每秒插入性能可能會低到幾百條。其實我以爲能手動切分數據庫就手動切分或者手動作歷史庫,不 要依賴這種自動化的Sharding,由於一開始數據就放到正確的位置比分隔再搬遷效率不知道高多少。我的認爲Mongodb單數據庫存儲不超過1億的數 據比較合適,再大仍是手動分庫吧。
4) 對於數據的插入,若是使用多線程並不會帶來性能的提升,反而還會降低一點性能(而且能夠在http接口上看到,有大量的線程處於等待)。
5) 在整個測試過程當中,批量插入的時候遇到過幾回鏈接被遠程計算機關閉的錯誤,懷疑是有的時候Mongodb不穩定關閉了鏈接,或是官方的C#客戶端有BUG,可是也僅僅是在數據量特別大的時候遇到幾回。
最新補充:在以後我又進行了幾天測試,把測試數據量進一步加大到5億,總磁盤佔用超過500G,發現和2億數據量相比,全部性能都差很少,只是測試6和測試7在超過2億級別數據以後,每400萬記錄做爲一個循環,上下波動30%的性能,很是有規律。