5

所以我在我的数据库中有一个带有以下 shardkey 的集合: {cl:"yyyy-mm", user_id:N}

当我执行以下查询时

db.collection.find({ cl:"2012-03", user_id:{$in:users}, _id:{"$lt":new ObjectId('4f788b54204cfa4946000044')} }).sort({_id:-1}).limit(5).explain(true)

它给了我这个结果:

"clusteredType" : "ParallelSort",
"shards" : {
    "set1/hostname.com:27018" : [
        {
            "cursor" : "BtreeCursor cl_1_user_id_1 multi",
            "nscanned" : 21294,
            "nscannedObjects" : 21288,
            "n" : 5,
            "scanAndOrder" : true,
            "millis" : 1258,
            "nYields" : 69,
            "nChunkSkips" : 0,
            "isMultiKey" : false,
            "indexOnly" : false,
            "indexBounds" : { ...

那么如何进行排序以使用索引,然后我不需要扫描所有 21288 个文档,只需返回最后 5 个?

4

4 回答 4

4

感谢您对 Dex 的关注!

如果在 2013 年还不算太晚,我建议在这里避免 scanAndOrder 的索引是 { _id: -1, cl: 1, user_id: 1 }。

原因是因为 _id 上的 $lt 和 user_id 上的 $in 构成了跨多个索引“桶”的范围。除上述顺序之外的任何其他顺序的索引意味着这些存储桶仍必须一起排序以满足对 _id 的排序。通过将 _id 放在首位,索引中访问的所有文档将提前正确排序。

请注意,这比 Andre 的建议({ _id: -1, user_id: 1, cl: 1 },这也应该避免 scanAndOrder)略有改进,因为它允许对 cl 进行直接等效检查以修剪结果。

查看http://blog.mongolab.com/2012/06/cardinal-ins/了解更多详情。

于 2013-08-07T21:15:30.530 回答
3

因为您使用的是$lt我不知道您是否能够scanAndOrder从查询中删除操作。常规公理是您将排序字段作为索引的最后一个成员,但这会在进行范围查询的地方分解。您通常可以通过反转索引的顺序,将排序字段作为第一个成员插入来解决此问题。在您的情况下还有一个问题,因为集合是分片的,因此您可能总是至少选择分片键索引而不是“倒序索引”。

在不了解数据分布的情况下,很难推荐具体的行动方案。从我所做的简短测试中,添加一个索引以将and{cl:1,user_id:1,_id:-1}大致减半。这将使用排序字段作为索引的最后一个成员,但存在如上所述的缺陷。您也可以尝试与此相反,但您可能会发现分片键将在该索引上被选中。您可以进一步尝试通过提示强制反向索引,但这并没有在我的测试中带来任何性能提升。nscannednscannedobjects{_id:1,user_id:1,cl:-1}

于 2012-08-10T07:12:59.323 回答
2

尝试 Dex 以确保索引按照您认为的方式工作:https ://github.com/mongolab/dex

于 2012-08-08T19:36:35.697 回答
1

AFAIK、条件运算符 $gt、$lt 等(mongo 查询中的比较函数)使 mongo根本不使用索引(对于查询的那一部分)。Mongo 必须扫描集合中的所有文档才能执行此类查询。

所以,这部分:

find({ cl:"2012-03", user_id:{$in:users}, _id:{"$lt":new ObjectId('4f788b54204cfa4946000044')} })

不会使用索引,即使它存在。这让它变慢了。

UPDATE 查询中的条件运算符 $gt、$lt 等允许使用索引,但没有它们时效率不高。仍然 mongodb 必须扫描更多返回结果的文档。

于 2012-11-23T16:13:26.133 回答