3

** 更新 **

我发布了一个答案,因为它已被确认是一个问题

** 原来的 **

首先,我很抱歉——我昨天才开始使用 MongoDB,对此我还是很陌生。我有一个非常简单的查询,使用 PHP 我的发现是这样的:

Mongo 版本是 2.0.4,在 CentOS 6.2 (Final) x64 上运行

$start = microtime(true);
$totalactive = $db->people->count(array('items'=> array('$gt' => 1)));
$end = microtime(true);
printf("Query lasted %.2f seconds\n", $end - $start);

没有索引,它返回:

Query lasted 0.15 seconds

我的数据库中有 280,000 条记录。所以我认为在“项目”上添加索引应该会有所帮助,因为我经常查询这些数据。但令我难以置信的是,在添加索引后,我得到了这个:

Query lasted 0.25 seconds

我做错什么了吗?

我用 find 而不是 count 来得到解释,这是输出:

> db.people.find({ 'items' : { '$gte' : 1 } }).explain();
{
"cursor" : "BtreeCursor items_1",
"nscanned" : 206396,
"nscannedObjects" : 206396,
"n" : 206396,
"millis" : 269,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {
    "items" : [
        [
            1,
            1.7976931348623157e+308
        ]
    ]
}
}

如果我将查询更改为“$ne”0,则需要多花 10 毫秒!

以下是收集统计数据:

> db.people.stats()
{
"ns" : "stats.people",
"count" : 281207,
"size" : 23621416,
"avgObjSize" : 84.00009957077881,
"storageSize" : 33333248,
"numExtents" : 8,
"nindexes" : 2,
"lastExtentSize" : 12083200,
"paddingFactor" : 1,
"flags" : 0,
"totalIndexSize" : 21412944,
"indexSizes" : {
    "_id_" : 14324352,
    "items_1" : 7088592
},
"ok" : 1
}

我有 1GB 的可用内存,所以我相信索引适合内存。

根据要求,这是人员索引:

> db.people.getIndexes()
[
{
    "v" : 1,
    "key" : {
        "_id" : 1
    },
    "ns" : "stats.people",
    "name" : "_id_"
},
{
    "v" : 1,
    "key" : {
        "items" : 1
    },
    "ns" : "stats.people",
    "name" : "items_1"
}
]
4

5 回答 5

1

拥有索引可能有两个好处:

  1. 仅访问集合的一小部分时(因为索引可以满足限制性过滤器)。经验法则小于 10%。

  2. 当根本不需要访问集合时(因为所有必要的数据都在索引中,用于过滤和结果集)。这将由“indexOnly = true”指示。

对于“查找”查询,这两种情况都不正确:您正在访问几乎整个集合(281207 中的 206396)并且需要所有字段数据。因此,您将首先浏览索引,然后再浏览几乎整个集合,从而违背了索引的目的。只是阅读整个集合会更快。

我本来希望“计数”查询执行得更好(因为只需通过索引就可以满足)。你也能解释一下吗?

于 2012-04-13T02:53:31.103 回答
1

看这个:

http://www.mongodb.org/display/DOCS/Indexing+Advice+and+FAQ#IndexingAdviceandFAQ-5.MongoDB%27s%24neor%24ninoperator%27saren%27tefficientwithindexes

这让我考虑了这个解决方案。这个怎么样?

$totalactive = $db->people->count() - $db->people->count(array('items'=> array('$eq' => 1)));
于 2012-04-13T03:10:21.760 回答
1

这被确认是一个错误或需要在 MongoDB 引擎中优化的东西。我在 mongo 邮件列表中发布了这个,并从 Eliot Horowitz 收到了回复

这绝对是一个错误,或者至少是可以更好地优化的路径。做了一个案例:https ://jira.mongodb.org/browse/SERVER-5607

Priority:  Major
Fix Version/s: 2.3 desired
Type:  Bug

感谢那些帮助确认这是一个错误的人=)

于 2012-04-15T13:41:21.010 回答
0

你能提供一个这个集合中的对象的例子吗?“项目”字段是一个数组?如果是这样,我建议您添加一个新字段“itemCount”并在其上放置一个索引。在此字段上执行 $gt 将非常快。

于 2012-04-13T13:48:43.563 回答
0

这是因为您的查询是近乎完整的集合扫描。查询优化器选择使用索引,而它不应该使用它来获得最佳性能。是的,这是违反直觉的,但这是因为光标正在遍历索引 b 树并获取树指向的文档,如果必须扫描几乎整个树,这比仅遍历集合要慢。

如果你真的需要做这种查询,并且你想将该索引用于其他事情,比如排序,你可以使用.hint({$natural: 1}), 告诉查询不要使用索引。

巧合的是,我最近在一篇博文中发布了一个类似的问题:http ://wes.skeweredrook.com/testing-with-mongodb-part-1/

于 2012-04-14T23:11:36.703 回答