MongoDB查询性能验证及优化

结论:
1、 200w数据,合理使用索引的情况下,单个stationId下4w数据。mongodb查询和排序的性能理想,无正则时client可以在600ms+完成查询,qps300+。有正则时client可以在1300ms+完成查询,qps140+。
2、 Mongodb的count性能比较差,非并发情况下client可以在330ms完成查询,在并发情况下则需要1-3s。可以考虑估算总数的方法,http://blog.sina.com.cn/s/blog_56545fd30101442b.html

测试环境:mongodb使用 replica set,1主2从,96G内存,版本2.6.5
Mem消耗(4个200w数据的collection):


空间消耗(测试数据最终选定的collection):


Jvm: -Xms2G -Xmx2G
Ping延迟33ms
查询都使用ReadPreference.secondaryPreferred()

无正则

1、 创建stationId, firmId复合引查询场景(200w集合,12个字段)
查询次数:20000
查询条件:多条件查询10条记录,并逐条获取记录
String key = "清泉" + r.nextInt(1000);
Pattern pattern = Pattern.compile(key);
BasicDBObject queryObject = new BasicDBObject("stationId",
new BasicDBObject("$in", new Integer[]{20}))
       .append("firmId", new BasicDBObject("$gt", 5000))
      .append("dealCount", new BasicDBObject("$gt", r.nextInt(1000000)));   DBCursor cursor = collection.find(queryObject).limit(10).skip(2);
并发:200
耗时:61566
单次耗时(server):124ms
Qps:324.85

2、 创建stationId, firmId复合引查询场景(200w集合,12个字段)
查询次数:20000
查询条件:多条件查询10条记录排序,并逐条获取记录
String key = "清泉" + r.nextInt(100);
Pattern pattern = Pattern.compile(key);
BasicDBObject queryObject = new BasicDBObject("stationId",
new BasicDBObject("$in", new Integer[]{4, 20}))
        .append("firmId", new BasicDBObject("$gt", 5000))
       .append("dealCount", new BasicDBObject("$gt", r.nextInt(1000000)));   DBCursor cursor = collection.find(queryObject)
.sort(new BasicDBObject("firmId", 1)).limit(10).skip(2);
并发:200
耗时:63187
单次耗时(server):119ms
Qps:316.52

3、 创建stationId, firmId复合引查询场景(200w集合,12个字段)
查询次数:2000
查询条件:多条件查询记录数
String key = "清泉" + r.nextInt(100);
Pattern pattern = Pattern.compile(key);
BasicDBObject queryObject = new BasicDBObject("stationId",
new BasicDBObject("$in", new Integer[]{4, 20}))
    .append("firmId", new BasicDBObject("$gt", 5000))
.append("dealCount", new BasicDBObject("$gt", r.nextInt(1000000)));  
long count = collection.count(queryObject);
并发:200
耗时:21887
单次耗时(client):280ms
Qps:91.38

有正则

4、 创建stationId, firmId复合引查询场景(200w集合,12个字段)
查询次数:20000
查询条件:多条件查询10条记录,并逐条获取记录
String key = "清泉" + r.nextInt(1000);
Pattern pattern = Pattern.compile(key);
BasicDBObject queryObject = new BasicDBObject("stationId",
new BasicDBObject("$in", new Integer[]{20}))
       .append("firmId", new BasicDBObject("$gt", 5000))
       .append ("dealCount", new BasicDBObject("$gt", r.nextInt(1000000)))
       .append("firmName", pattern);
   DBCursor cursor = collection.find(queryObject).limit(10).skip(2);
并发:200
耗时:137673
单次耗时(server):225ms
Qps:145.27

5、 创建stationId, firmId复合引查询场景(200w集合,12个字段)
查询次数:20000
查询条件:多条件查询10条记录排序,并逐条获取记录
String key = "清泉" + r.nextInt(1000);
Pattern pattern = Pattern.compile(key);
BasicDBObject queryObject = new BasicDBObject("stationId",
new BasicDBObject("$in", new Integer[]{4, 20}))
      .append("firmId", new BasicDBObject("$gt", 5000))
       .append ("dealCount", new BasicDBObject("$gt", r.nextInt(1000000)))
       .append("firmName", pattern);
   DBCursor cursor = collection.find(queryObject)
.sort(new BasicDBObject("firmId", 1)).limit(10).skip(2);
并发:200
耗时:138673
单次耗时(server):230ms
Qps:144.22

6、 创建stationId, firmId复合引查询场景(200w集合,12个字段)
查询次数:2000
查询条件:多条件查询记录数
String key = "清泉" + r.nextInt(1000);
Pattern pattern = Pattern.compile(key);
BasicDBObject queryObject = new BasicDBObject("stationId",
new BasicDBObject("$in", new Integer[]{4, 20}))
        .append("firmId", new BasicDBObject("$gt", 5000))
        .append ("dealCount", new BasicDBObject("$gt", r.nextInt(1000000)))
      .append("firmName", pattern);
   long count = collection.count(queryObject);
并发:200
耗时:23155
单次耗时(client):330ms
Qps:86.37



MongoDB索引特点
1、 复合索引必须命中首字段,否则无法生效。后面的字段可以不按顺序命中。
2、 复合索引字段越多占用空间越大,但对查询性能影响不大(数组索引除外)。
3、 会根据sort字段选择索引,优先级超过复合索引中的非首字段。
 






4、 命中复合索引的情况下,数据量<10w的情况下,过滤非索引字段,效率也比较高。



5、 全文检索性能比较差,200w数据命中50w的情况下,全文检索需要10+s,正则需要1s。
6、不走索引的字段,如果用于查询,创建schema尽量靠前,可以提升查询效率。
7、索引覆盖查询,如果检索条件,排序字段和查询字段都是索引中的字段,会省去从文档中Load数据的过程,查询会更快。

MongoDB客户端配置,可以提出来做成spring注入,设置最大连接数什么的。
MongoClientOptions options =
MongoClientOptions.builder().maxWaitTime(1000 * 60 * 2)
.connectionsPerHost(500).build();
mongoClient = new MongoClient(Arrays.asList(new ServerAddress("10.205.68.57", 8700),
new ServerAddress("10.205.68.15", 8700),
new ServerAddress("10.205.69.13", 8700)), options);
mongoClient.setReadPreference(ReadPreference.secondaryPreferred());


参考文档
《深入学习MongoDB》
http://blog.163.com/wm_at163/blog/static/132173490201110254257510/
http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-java-driver/#getting-started-with-java-driver
http://mongoing.com/tj/microshard-ycsb //压力测试
http://www.cnblogs.com/xusir/archive/2012/12/24/2830957.html //命令
http://www.361way.com/mongo-mem/1489.html //内存监控
http://www.oschina.net/translate/mongodb-text-search-tutorial?cmp 全文检索
http://www.oschina.net/translate/mongodb-indexing-tip-3-too-many-fields-to-index-use //复合索引
http://blog.sina.com.cn/s/blog_56545fd30101442b.html 分页

mongoDB调研_结论.docx为最终场景下的测试数据,分为有正则和无正则。
mongoDB调研_remote.docx为测试验证过程中的数据,有可能存在缓存等情况,不一定准确,功参考。

猜你喜欢

转载自mazhen2010.iteye.com/blog/2163704