进行了一下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%的性能,很是有规律。