结论:
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());