【问题标题】:MongoDB aggregration ignores index orderMongoDB聚合忽略索引顺序
【发布时间】:2014-05-08 11:25:17
【问题描述】:

为了从 MongoDB 中选择 100 个最新文档,其中每个文档由同一集合中具有相似字段的多个文档组成(在本例中为 timestamp),我使用以下系列查询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
});

我的印象是timestamp 上的第一个索引-1 意味着它们按降序索引,导致集合中第一个$limit 文档始终是具有最高timestamp 的文档.

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

【问题讨论】:

  • 你需要先排序才能得到想要的结果
  • @Sebastian 为什么?这意味着要对数千份文档进行分类。索引顺序是专门为防止这种开销而设计的,不是吗?
  • 不,它的存在是为了提高排序效率。这并不意味着您的所有查询都会自动排序。要使用索引,您需要进行排序。
  • 如果有限的选择不会自动限制在索引顺序中,那么为什么我们需要在索引中指定该顺序?
  • @Sebastian 如果仔细观察,问题显然在于 $match 选择找不到索引。排序与此无关。我看到了诱惑,但很可能“时间戳”实际上反映了插入顺序。因此,如果有的话,那应该是复合索引的辅助键。

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


【解决方案1】:

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

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

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

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

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

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

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

按照您要求的顺序。

【讨论】:

  • 感谢您的回复尼尔伦。不幸的是,我似乎不明白您的确切意思,因为我 正在 索引我正在查询的字段。此外,最重要的索引是timestamp,因为active 只是忽略某些记录的布尔值,这种机制没有问题。这就是选择文档的全部内容,timestamp-wise。因此,我可能误解了你。您能否详细说明并建议如何改进我的查询?
  • @Redsandro 希望我添加的另一个编辑可以解决这个问题。但最重要的是您在索引中指定字段的顺序需要与您查询它们的顺序相匹配。否则不选择索引。对于聚合的 $match 阶段非常重要。或者事实上的任何查询。
  • 如果我理解正确的话,我对indexorder想法如下:“否则索引可能效率不高" - 实际上是 - "否则索引将被完全忽略。" 你确定吗?这意味着不可能在匹配 active 以外的其他内容的情况下进行单独的查询,同时仍然享受索引的舒缓速度。
  • @Redsandro 绝对是。查看您在问题中指定的索引,以及它与我给出的定义有何不同。 MongoDB 中的查询优化器希望按照它们被引用的 order 访问字段,以便它选择该索引以供使用。您的 $match 位于 active 作为字段。因此,该字段 需要 成为所选索引的第一个元素。当然,除了我提到的情况,由于索引 API 的重大重构,我也提到了当前中断。谁又报告了这个问题? :)
  • 我现在明白了。拥有 3 字段复合索引并不意味着我可以查询任何索引字段。这意味着我必须准确选择顺序或索引中的字段。您的回答确实如此,但是当[我]对索引规则的理解不正确时,您的回答可以理解为:“重要的是使用(任何类型的)select(aka $match)语句first(在使用任何其他语句之前)”,我做了。感谢您耐心的回复。我会说它有点不同,但我很乐意接受!
【解决方案2】:

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

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

尽管 MongoDB 将创建四个每个 1 GB 的稀疏文件,但如果可用空间下降到 1 GB 以下,Mongo 仍然会阻塞。

如果发生这种情况,请释放至少 2 GB 并对数据进行碎片整理:

  • /etc/init.d/mongodb 停止

  • mongod --repair

  • /etc/init.d/mongodb start

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-22
    • 1970-01-01
    • 2016-10-20
    • 1970-01-01
    • 1970-01-01
    • 2013-12-09
    • 2017-01-24
    相关资源
    最近更新 更多