from:http://www.cnblogs.com/lovecindywang/archive/2011/03/02/1969324.html 
進行了一下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操作系統,兩者千兆網卡直連。

image

從這個測試可以看出,對于單進程的方式:

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%的性能,非常有規律。

作者:lovecindywang
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
分類: 開源
10
0
(請您對文章做出評價)
« 博主前一篇:淺談網絡客戶端的類庫編寫和使用
» 博主后一篇:Redis千萬級的數據量的性能測試
posted @ 2011-03-02 22:09 lovecindywang 閱讀(15352) 評論(44編輯 收藏
評論列表
  
#1樓 2011-03-02 23:14 blackcat  
存儲性能如何?
  
#2樓 2011-03-03 08:21 曾哲  
  
#3樓 2011-03-03 08:38 Kain  
非常好,正需要這方面的資料。
  
#4樓 2011-03-03 09:29 RealDigit  
沖著性能測試來的
  
#5樓 2011-03-03 09:43 new 維生素C.net()  
想問一下,Insert操作的數據是在調用客戶端里得出的還是在MongoDB上得出的?
  
#6樓 2011-03-03 09:52 姜敏  
樓主:您文章中的圖是用什么工具生成的?
  
#7樓[樓主2011-03-03 09:53 lovecindywang  
數據是客戶端生成的,圖是asp.net chart控件生成的
  
#8樓 2011-03-03 10:18 skyaspnet  
建議用表格來對結果進行總結,文字總結效果不太好
  
#9樓 2011-03-03 11:15 SeanC  
為什么Total Size(index+data)的值不是data size和index size之和?
每天數據1kb是純文本的大小嗎?包括json tag嗎?數據和索引各有幾列?
  
#10樓[樓主2011-03-03 11:31 lovecindywang  
@SeanC
1kb是包括tag后大概的大小,數據4列,其中3列做索引(2個數值和1個日期),另外一列存950字節。至于為什么Total Size(index+data)的值不是data size和index size之和?
我也清楚,這里的值取的是db.stats()中的storagesize/datasize和indexsize
  
#11樓 2011-03-03 19:55 forchenyun  
請教,查詢有熱點嗎?還是2億多內隨機?或者說熱點范圍是怎么樣的?
  
#12樓[樓主2011-03-03 19:59 lovecindywang  
@forchenyun
總是搜索總數據范圍的前10%,在插入數據的時候數字字段隨機選取525600內的一個值。
  
#13樓 2011-03-08 19:04 shawny  
4) 對于一列條件的查詢,性能一直比較穩定,別小看,每秒能有8000-9000的查詢次數,每次返回10KB,相當于每秒查詢80MB數據,而且數據庫記錄是2億之后還能維持這個水平,性能驚人。

我覺得你這里說的“每秒能有8000-9000的查詢次數”,查詢到的是索引數據,還沒有真正去讀數據回來,例如:如果你用java client api寫的測試程序,如果調用find(query)去做測試,其實這里面返回的是DBCursor,而不是Document,需要繼續調用next()才返回數據;如果調用findOne(query)去做的測試,它返回的則是Document。

所以我覺得你說每秒查詢80MB的數據有問題,因為每次沒有返回10KB回來。
  
#14樓[樓主2011-03-08 21:29 lovecindywang  
@shawny
不存在這個問題,我是ToList()的
  
#15樓 2011-03-08 22:25 shawny  
@lovecindywang
謝謝你的回復~
因為我這邊是用java寫的benchmark測試程序,當我使用find(query)會有很高的qps,因為這個僅僅返回的是DBCursor信息,而不是真正的數據,不是我想要的。當我用findOne(query)的時候qps就慘目忍睹了。

所以還是要問幾個問題:

1) 你測試的讀操作是順序讀還是隨機讀?
2)關于測試5“查詢兩個索引后的數字列,返回10條記錄(每條記錄只返回20字節左右的2個小字段)的性能,這個測的是返回小數據量以及多一個查詢條件對性能的影響”,按照對應圖形來看,每秒鐘大概讀的數據是10000qps * 20byte * 10條記錄 = 2M/s,這個速度和測試4比就相差很多了啊!
3)按照測試4給出的結果,在數據量遠遠大于內存大小的時候,讀性能還是勝過寫性能。
4)測試讀的時候,client端開了多少個線程?
  
#16樓[樓主2011-03-09 09:23 lovecindywang  
1) 順序讀
2) 一條記錄是1K而不是20字節哦
3)使用并行庫,所以我估計是8個線程,對應八核
  
#17樓 2011-03-09 09:43 shawny  
@lovecindywang
下面是正文中的引用:
“5) 查詢兩個索引后的數字列,返回10條記錄(每條記錄只返回20字節左右的2個小字段)的性能,這個測的是返回小數據量以及多一個查詢條件對性能的影響”

另外我看到的關于mongodb的測試"MongoDB Performance for more data than memory", 他的測試結果在“https://spreadsheets.google.com/ccc?key=0At_QEIJCB2GbdEI3d3VBR2hFY1AyVTVNY0N6OGh5dXc&hl=en#”可以看看第六行,從1000w條記錄中讀取30000條大概花了74.09s的時間,那么qps就是30000/74.09 = 404

有什么問題,歡迎糾正。
  
#18樓[樓主2011-03-09 09:57 lovecindywang  
我的測試網絡延遲以及帶寬都不考慮的,是千兆直連
性能上可能是會有差距
  
#19樓 2011-03-09 10:12 shawny  
@lovecindywang
其實我的測試環境也是千兆直連的,讀性能不理想。
  
#20樓[樓主2011-03-09 10:17 lovecindywang  
@shawny
不知服務器配置是怎么樣的? 可能配置不是太高吧
  
#21樓 2011-03-09 10:18 shawny  
@lovecindywang
另外不知道你看沒看讀操作時候的iostat信息,rMB/s
  
#22樓 2011-03-09 10:44 shawny  
@lovecindywang
服務器的內存肯定沒你這么大(我的是8G),我測試數據量是20G(2000w條記錄),但是我主要是想看一下當數據量遠遠大于內存的時候,性能如何。

雖然你的內存是24G,但是你的記錄也達到了2億條,所以數據量也已經遠遠大于內存了。
  
#23樓[樓主2011-03-09 10:54 lovecindywang  
測試機HP DL180 G6,RAID 10兩組,6塊 600G SAS一組,可能IO性能好很多的關系
  
#24樓 2011-03-09 11:11 shawny  
@lovecindywang
你服務器用的是raid啊,我擦!!! 我的是SCSI磁盤!
當Mongodb處理讀操作的時候,用iostat查看util%一直處于100%,說明mongodb一直在忙活IO操作呢,rMB/s很低!
所以我這里性能低主要是因為磁盤IO太爛!
  
#25樓[樓主2011-03-09 11:16 lovecindywang  
我測試的時候util% 沒有超過30%過 呵呵
  
#26樓 2011-03-09 11:19 shawny  
@lovecindywang
差距啊!謝謝你的回復!
  
#27樓 2011-03-21 10:41 一線風  
這么牛嗎?回頭找個項目用一下看看。
  
#28樓 2011-04-14 09:36 Weiseditor  
奇怪,怎么Mongodb的插入跟更新,都不用判斷成功或失敗!!!
  
#29樓[樓主2011-04-14 09:59 lovecindywang  
@Weiseditor
如果追求高可靠性,性能肯定會有所下降,你說的功能可以通過SafeMode的Insert或Update實現,確保所有修改應用到多個Node
  
#30樓 2011-04-16 10:20 Weiseditor  
@lovecindywang
我本來是用SafeMode 反回的 我用的是C#

他有個SafeModelResult r = Coll.Insert(xxx);

if (r.Ok == true/false)

但是提示 未將對象引用設置到對象的實例...

數據也沒有插入進去,但是直接寫成 Coll.Insert(xxx); 卻插入成功!

很奇怪.
  
#31樓 2011-08-11 13:55 木乃伊  
學習了,圖真不錯。
  
#32樓 2011-08-30 10:51 itmuse  
在生產環境中,mongodb安裝在red hat 64位的linux中,client端是windows 2003的官方的c#的driver,mongodb的數據達到了40g以上,鏈接經常會被mongod的服務端自動關閉,查看日志是:SocketException in connThread, closing client connection。另外,寫入的并發大概10/second,查詢的并發大概25/second,請問有沒有遇到這樣的情況,這問題糾結了很久,一直找不到原因。
  
#33樓 2011-09-05 19:04 酷&酷  
mongod 集群能不能用戶認證 ?
  
#34樓 2011-09-23 09:07 zsea  
如何數據不多可以用這個,我試過插入10億條記錄,最后系統崩潰了。
  
#35樓 2011-10-24 19:28 小蝦米  
很好的測試哦,對mongodb充滿信心
  
#36樓 2011-11-16 17:19 寒風吹過  
為什么我在做MONGODB的壓力測試時(頁面直連MONGDB),測試結果的寫入性能很底啊。只有每秒10個左右的寫入。為什么差距這大?有可能是哪方面的原因引起的,求指教!
  
#37樓[樓主2011-11-19 17:56 lovecindywang  
@寒風吹過
什么驅動? 懷疑是不是每一次都使用了新的連接池?
  
#38樓 2011-11-21 10:15 寒風吹過  
是的,每次都使用了新的連接池。一個頁面訪問產生一個連接池

那有什么辦法提高效率呢?連接池做全局靜態變量?
  
#39樓[樓主2011-11-21 10:25 lovecindywang  
@寒風吹過
我不知道你事什么驅動,應該驅動把連接池對開發透明,在內部會維護一個連接字符串具有唯一連接池的!如果你不確定的話,把連接池或類似server的入口對象設置為靜態
  
#40樓 2011-11-21 11:08 寒風吹過  
//封裝類
public class MyMongoDb
{
private Mongo _mongo;
private IMongoDatabase _db;
public MyMongoDb() : this("Server=" + ConfigurationManager.AppSettings["MongoSevers"].ToString() + ";ConnectTimeout=500000;ConnectionLifetime=400000;MinimumPoolSize=10;MaximumPoolSize=500;Pooled=true", ConfigurationManager.AppSettings["MongoDB"].ToString())
{
}

/// <summary>
/// 獲取當前連接數據庫的指定集合【依據類型】
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IMongoCollection<T> GetCollection<T>() where T : class
{
return this.CurrentDb.GetCollection<T>();
}
}

客戶端調用:
using (MyMongoDb mm = new MyMongoDb())
{
mm.GetCollection<T>().Insert(o);
}
  
#41樓 2011-11-21 11:10 寒風吹過  
需要把這種方式改成連接字符串為靜態變量的方式嗎?
如果是靜態變量的話,多線程并發會存在問題不?
  
#42樓 2011-12-06 20:22 和歌  
請問可以將源碼給一份嗎,謝了!
zihuangning@gmail.com
  
#43樓 2012-02-02 18:17 javacty  
非常感謝您的博文,源代碼能提供個附件下載或者發我郵箱(javac112358@163.com)嗎?
  
#44樓 2012-02-20 12:07 研究員  
MongoDB的優勢在于內存與分布式處理,沒有個十幾臺機器,測不出它的優勢的。