【问题标题】:MongoDB find has the same speed with and without indexMongoDB find 有无索引速度一样
【发布时间】:2013-07-04 08:12:39
【问题描述】:

我使用 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

【问题讨论】:

    标签: java spring mongodb spring-mvc


    【解决方案1】:

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

    【讨论】:

    • 我想过原因,但很明显,你不能让用户只按FIRSTNAME排序,然后按LASTNAME排序。如果 100 个用户中有 50 个具有相同的 FIRSTNAME,那么您可能错过了很多应该在 LASTNAME 的这 50 个原因中。
    【解决方案2】:
    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: 排序持续时间超过 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版本?

    希望这能回答你的问题。

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

    【讨论】:

    • 我没有明确设置 scanAndOrder。我如何使用 mongoTemplate?有什么想法吗?
    • Nabor:给我一些时间或阅读我提到的链接。它有你问题的所有答案
    • 查看我对答案的评论,我标记为正确。没有组合索引,结果就不可能正确。
    猜你喜欢
    • 2015-04-06
    • 2015-03-26
    • 1970-01-01
    • 2014-12-15
    • 1970-01-01
    • 1970-01-01
    • 2015-08-29
    • 2014-11-13
    • 1970-01-01
    相关资源
    最近更新 更多