在我的MongoDB查询结果中删除“scanAndOrder”:true

时间:2012-08-08 18:51:18

标签: mongodb sorting indexing sharding

所以我的数据库中有一个带有以下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 个答案:

答案 0 :(得分:4)

感谢Dex的呐喊!

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

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

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

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

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

尝试使用Dex确保索引按照您认为的方式运行:https://github.com/mongolab/dex

答案 3 :(得分:1)

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

所以,这部分:

  

find({cl:“2012-03”,user_id:{$ in:users},_ id:{“$ lt”:new   ObjectId('4f788b54204cfa4946000044')}})

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

<强>更新 查询中的条件运算符$ gt,$ lt等允许使用索引,但没有它们那么高效。仍然mongodb必须扫描更多返回结果的文档。