MongoDB聚合忽略索引顺序

时间:2014-05-08 11:25:17

标签: javascript node.js mongodb mongodb-query aggregation-framework

为了从MongoDB中选择100个最新文档,其中每个文档由同一个集合中具有相似字段的多个文档组成(在本例中为timestamp),I' m使用Node.js中的以下一系列查询:

        return q.ninvoke(collection, 'aggregate',
            [
                {
                    $match  : { active: true }
                },
                {
                    $limit  : 100
                },
                {
                    $group  : {
                        _id         : "$timestamp",
                        mintime : {
                            $min        : "$seconds"
                        },
                        timestamp   : {
                            $first      : "$timestamp"
                        },
                        data        : {
                            $first      : "$data"
                        }
                    }
                }
            ]);

当集合中的文档少于$limit时,这样可以正常工作。如果有更多,则选择最旧的文档(先插入),而不是选择timestamp最高的文档(通常但不总是最后插入的文档)。

这是意料之外的,因为文档会使用以下保证索引插入到集合中:

collection.ensureIndex({
    timestamp   : -1,
    seconds     : -1,
    active      : -1
}, {
    sparse : false
});

我的印象是,-1上的timestamp第一个索引意味着它们按降序编入索引,从而形成一个集合,其中第一个$limit文档始终是最高timestamp

为什么这不能按预期工作?
我错了吗?

2 个答案:

答案 0 :(得分:1)

实际上你真正的问题是没有选择索引。您可以通过调用聚合的explain形式,通过db.runCommand选项(MongoDB 2.6中提供或实际来自MongoDB 2.4.9,但未记录)来检查。

使用MongoDB,当匹配第一个时,指定要在索引中使用的字段非常很重要。所以索引定义为:

collection.ensureIndex({ "active": 1 })

在这种情况下,即使-1被选中也是如此。您的索引不会,因为您没有引用任何其他字段。

这个可以强制进行更大的选择,当优化器认识到这将是最佳情况时,但在当前2.6版本中这实际上似乎是broken (直到修复)。

附录:因此可能涉及“排序”组件,但更多的是关于如何再次指定复合索引。为了确保分组边界的“时间戳”值,请确保在初始选择器之后包含该值,如:

collection.ensureIndex({ "active": -1, "timestamp": -1 })

按照您要求的顺序。

答案 1 :(得分:0)

补充@NeilLunn给出的非常重要的答案:

我不知道技术细节,但即使是正确的声明也可以从索引中始终选择错误的文档,如果你的磁盘空间“低”。 Mongo甚至可能没有抱怨这个,它会只选择错误的文件。

即使MongoDB会创建四个千兆字节的稀疏文件,如果可用空间下降低于千兆字节,Mongo仍然会窒息。

如果发生这种情况,请释放至少两千兆字节并对数据进行碎片整理:

  • /etc/init.d/mongodb stop

  • mongod --repair

  • /etc/init.d/mongodb start

根据经验,我会说:始终保持至少2̶G̶B̶2+ 4 = 6GB免费。