0

我使用 Spring 中的 MongoTemplate 来访问 MongoDB。

final Query query = new Query(Criteria.where("_id").exists(true));
query.with(new Sort(Direction.ASC, "FIRSTNAME", "LASTNAME", "EMAIL"));
if (count > 0) {
    query.limit(count);
}
query.skip(start);
query.fields().include("FIRSTNAME");
query.fields().include("LASTNAME");
query.fields().include("EMAIL");
return mongoTemplate.find(query, User.class, "users");

我在我的 MongoDB 中生成了 400.000 条记录。在不使用上面写的排序行的情况下询问前 25 个用户时,我在不到 50 毫秒内得到了结果。

使用排序它持续超过 4 秒。

然后我为 FIRSTNAME、LASTNAME、EMAIL 创建了索引。单一索引,而不是组合索引

mongoTemplate.indexOps("users").ensureIndex(new Index("FIRSTNAME", Order.ASCENDING));
mongoTemplate.indexOps("users").ensureIndex(new Index("LASTNAME", Order.ASCENDING));
mongoTemplate.indexOps("users").ensureIndex(new Index("EMAIL", Order.ASCENDING));

创建这些索引后,查询再次持续超过 4 秒。

我的错误是什么?

-- 编辑 MongoDB 在控制台上写这个...

Thu Jul 04 10:10:11.442 [conn50] query mydb.users query: { query: { _id: { $exists: true } }, orderby: { LASTNAME: 1, FIRSTNAME: 1, EMAIL: 1 } } ntoreturn:25 ntoskip:0 nscanned:382424 scanAndOrder:1 keyUpdates:0 numYields: 2 locks(micros) r:6903475 nreturned:25 reslen:3669 4097ms
4

2 回答 2

1

您必须按此顺序为 , 和 , 创建一个复合索引FIRSTNAMELASTNAME并且EMAIL所有这些都使用升序。

于 2013-07-04T08:33:57.963 回答
0
Thu Jul 04 10:10:11.442 [conn50] query mydb.users query: 
{ query: { _id: { $exists: true } }, orderby: { LASTNAME: 1, FIRSTNAME: 1, EMAIL: 1 } } 
ntoreturn:25 ntoskip:0 nscanned:382424 scanAndOrder:1 keyUpdates:0 numYields: 2 
locks(micros) r:6903475 nreturned:25 reslen:3669 4097ms

可能的不良迹象:

您的 scanAndOrder 正在实现(scanAndOrder=1),如果我错了,请纠正我。

它必须返回(ntoreturn:25) 表示 25 个文档,但它正在扫描 ( nscanned:382424) 382424 个文档。

索引查询,nscanned 是 Mongo 扫描的范围内的索引键数,nscannedObjects 是它查看的文档数以得到最终结果。nscannedObjects 至少包括所有返回的文档,即使 Mongo 可以通过查看索引来判断该文档肯定是匹配的。因此,您可以看到 nscanned >= nscannedObjects >= n 始终。

问题的背景:

案例 1:在不使用上面写的排序行的情况下询问前 25 个用户时,我在不到 50 毫秒内得到了结果。

案例 2:使用 sort 持续超过 4 秒。

query.with(new Sort(Direction.ASC, "FIRSTNAME", "LASTNAME", "EMAIL"));

在这种情况下,没有索引:所以它按照这里提到的那样做:

这意味着 MongoDB 必须在内存中批量处理所有结果,对它们进行排序,然后返回它们。不法行为比比皆是。首先,它会消耗服务器上的 RAM 和 CPU。此外,Mongo 不是将我的结果分批流式传输,而是一次将它们全部转储到网络上,从而对我的应用服务器上的 RAM 征税。最后,Mongo 对将在内存中排序的数据实施了 32MB 的限制。

案例 3:为 FIRSTNAME、LASTNAME、EMAIL 创建索引。单一索引,而不是组合索引

我猜它仍然没有从索引中获取数据。您必须根据排序顺序调整索引

排序字段(升序/降序仅在有多个排序字段时才重要)

Add sort fields to the index in the same order and direction as your query's sort

有关更多详细信息,请查看此 http://emptysqua.re/blog/optimizing-mongodb-compound-indexes/

可能的答案:

在查询orderby: { LASTNAME: 1, FIRSTNAME: 1, EMAIL: 1 } }中排序的顺序与您在中指定的顺序不同:

mongoTemplate.indexOps("users").ensureIndex(new Index("FIRSTNAME", Order.ASCENDING));
mongoTemplate.indexOps("users").ensureIndex(new Index("LASTNAME", Order.ASCENDING));
mongoTemplate.indexOps("users").ensureIndex(new Index("EMAIL", Order.ASCENDING));

我猜 Spring API 可能不会保留订单: https ://jira.springsource.org/browse/DATAMONGO-177

当我尝试对多个字段进行排序时,不会保持字段的顺序。Sort 类使用 HashMap 而不是 LinkedHashMap,因此不能保证它们返回的顺序。

你能提到spring jar版本吗?

希望这能回答你的问题。

纠正我你觉得我可能错的地方,因为我有点生疏。

于 2013-07-04T08:24:49.977 回答