MongoDB慢速查询

MongoDB慢速查询,mongodb,Mongodb,我有一个非常简单的mongo查询,应该使用_id索引。 解释计划看起来不错: > db.items.find({ deleted_at: null, _id: ObjectId('541fd8016d792e0804820100') }).sort({ positions: 1 }).explain() { "cursor" : "BtreeCursor _id_", "isMultiKey" : false, "n" : 1, "nscannedObjec

我有一个非常简单的mongo查询,应该使用_id索引。 解释计划看起来不错:

> db.items.find({ deleted_at: null, _id: ObjectId('541fd8016d792e0804820100') }).sort({ positions: 1 }).explain()
{
    "cursor" : "BtreeCursor _id_",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 1,
    "nscannedObjectsAllPlans" : 6,
    "nscannedAllPlans" : 7,
    "scanAndOrder" : true,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "_id" : [
            [
                ObjectId("541fd8016d792e0804820100"),
                ObjectId("541fd8016d792e0804820100")
            ]
        ]
    },
    "server" : "mydbserver:27017",
    "filterSet" : false
}
但当我执行查询时,它会在100-800毫秒内执行:

> db.items.find({ deleted_at: null, _id: ObjectId('541fd8016d792e0804820100') }).sort({ positions: 1 })
2014-09-26T12:34:00.279+0300 [conn38926] query mydb.items query: { query: { deleted_at: null, _id: ObjectId('541fd8016d792e0804820100') }, orderby: { positions: 1.0 } } planSummary: IXSCAN { positions: 1 } ntoreturn:0 ntoskip:0 nscanned:70043 nscannedObjects:70043 keyUpdates:0 numYields:1 locks(micros) r:1391012 nreturned:1 reslen:814 761ms
为什么它报告nscanned:70043 nscannedObjects:70043,为什么它这么慢

我在CentOS 6上使用MongoDB 2.6.4

我尝试修复MongoDB,完全转储/导入,没有帮助

更新1

我在上没有已删除\u的索引,但在\u id上有索引

更新2 2014-09-26 14:57 EET

在_id、deleted _at上添加索引没有帮助,甚至解释也不使用该索引:

> db.items.ensureIndex({ _id: 1, deleted_at: 1 }, { unique: true })
> db.items.getIndexes()
[
    {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "mydb.items"
    },
    {
        "v" : 1,
        "unique" : true,
        "key" : {
            "_id" : 1,
            "deleted_at" : 1
        },
        "name" : "_id_1_deleted_at_1",
        "ns" : "mydb.items"
    }
]
> db.items.find({ deleted_at: null, _id: ObjectId('541fd8016d792e0804820100') }).sort({ positions: 1 }).explain()
{
    "cursor" : "BtreeCursor _id_",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 1,
    "nscannedObjectsAllPlans" : 7,
    "nscannedAllPlans" : 8,
    "scanAndOrder" : true,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "_id" : [
            [
                ObjectId("541fd8016d792e0804820100"),
                ObjectId("541fd8016d792e0804820100")
            ]
        ]
    },
    "server" : "myserver:27017",
    "filterSet" : false
}
更新3 2014-09-26 15:03:32 EET

在_id上添加索引,删除_at,位置有帮助。但之前的案例强制进行完整的收集扫描,这看起来仍然很奇怪

> db.items.ensureIndex({ _id: 1, deleted_at: 1, positions: 1 })
> db.items.find({ deleted_at: null, _id: ObjectId('541fd8016d792e0804820100') }).sort({ positions: 1 }).explain()
{
    "cursor" : "BtreeCursor _id_1_deleted_at_1_positions_1",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 1,
    "nscannedObjectsAllPlans" : 3,
    "nscannedAllPlans" : 3,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "_id" : [
            [
                ObjectId("541fd8016d792e0804820100"),
                ObjectId("541fd8016d792e0804820100")
            ]
        ],
        "deleted_at" : [
            [
                null,
                null
            ]
        ],
        "positions" : [
            [
                {
                    "$minElement" : 1
                },
                {
                    "$maxElement" : 1
                }
            ]
        ]
    },
    "server" : "myserver:27017",
    "filterSet" : false
}

我猜您有70043个id为“541fd8016d792e0804820100”的对象。你能简单地在那个id上做一个查找并数一数吗?索引并不意味着“唯一”索引-如果您有一个具有特定值的索引存储桶,一旦到达该存储桶,它现在会在该存储桶内进行扫描,查看每个“已删除”值是否为“null”。为了解决这个问题,使用id的复合索引,deleted_at.

我猜您有70043个id为“541fd8016d792e0804820100”的对象。你能简单地在那个id上做一个查找并数一数吗?索引并不意味着“唯一”索引-如果您有一个具有特定值的索引存储桶,一旦到达该存储桶,它现在会在该存储桶内进行扫描,查看每个“已删除”值是否为“null”。要解决这个问题,请使用id的复合索引,deleted_at.

更新:更正了我建议使用id的答案,deleted_at复合索引。此外,在注释中,更清楚地说明在某些情况下解释可能无法反映查询计划器

我们所期望的是find将过滤结果,然后排序将应用于过滤集。但是,根据doc的规定,查询计划器不会使用_id上的索引,也不会使用此查询的职位上的索引(如果有)。现在,如果您在_id,position上有一个复合索引,那么它应该能够使用该索引来处理查询

要点是,如果您有一个索引支持您的查询,那么您可以确信查询计划器正在使用您的索引。在您的情况下,查询肯定没有被覆盖,如解释计划中的indexOnly:false所示

如果这是有意为之,那么它显然是违反直觉的,你应该提交一份bug报告,以便社区对这种特殊行为得到更详细的解释。

更新:更正了我的答案,建议在复合索引中使用一个_id,删除_。同样在评论中,在某些情况下,解释可能无法反映查询计划器

我们所期望的是find将过滤结果,然后排序将应用于过滤集。但是,根据doc的规定,查询计划器不会使用_id上的索引,也不会使用此查询的职位上的索引(如果有)。现在,如果您在_id,position上有一个复合索引,那么它应该能够使用该索引来处理查询

要点是,如果您有一个索引支持您的查询,那么您可以确信查询计划器正在使用您的索引。在您的情况下,查询肯定没有被覆盖,如解释计划中的indexOnly:false所示


如果这是出于设计,那么它显然是违反直觉的,因此,您应该提交一份bug报告,以便社区对这种特殊行为得到更详细的解释。

这看起来像一个bug。查询计划器应该选择_id索引,_id索引应该是您所需要的全部,因为它必须立即将结果集缩减为一个文档。排序应该是不相关的,因为它排序的是一个文档。这是一种奇怪的情况,因为您明确要求一个文档具有_id匹配,然后对其进行排序。您应该能够绕过mongoid并放弃排序作为解决方法

.explain不忽略排序。您可以这样简单地进行测试:

> for (var i = 0; i < 100; i++) { db.sort_test.insert({ "i" : i }) }
> db.sort_test.ensureIndex({ "i" : 1 })
> db.sort_test.find().sort({ "i" : 1 }).explain()
如果MongoDB不能使用索引进行排序,它将在内存中进行排序。explain输出中的字段scanander告诉您MongoDB是否不能使用索引对查询结果进行排序,即scanander:false表示MongoDB可以使用索引对查询结果进行排序

你能在办公室里提交一份错误报告吗?也许工程师会说它按设计工作,但在我看来,这种行为是错误的,而且在2.6.4中已经出现了一些查询规划器的问题。如果之前说过,我可能会错过它,但是:null处是否存在已删除的_会影响问题吗


另外,如果你提交了一张罚单,请在你的问题中贴一个链接,或者作为对这个答案的评论,以便其他人可以很容易地跟随。谢谢

这看起来像个bug。查询计划器应该选择_id索引,_id索引应该是您所需要的全部,因为它必须立即将结果集缩减为一个文档。排序应该是无关的,因为它的顺序是o ne文件。这是一种奇怪的情况,因为您明确要求一个文档具有_id匹配,然后对其进行排序。您应该能够绕过mongoid并放弃排序作为解决方法

.explain不忽略排序。您可以这样简单地进行测试:

> for (var i = 0; i < 100; i++) { db.sort_test.insert({ "i" : i }) }
> db.sort_test.ensureIndex({ "i" : 1 })
> db.sort_test.find().sort({ "i" : 1 }).explain()
如果MongoDB不能使用索引进行排序,它将在内存中进行排序。explain输出中的字段scanander告诉您MongoDB是否不能使用索引对查询结果进行排序,即scanander:false表示MongoDB可以使用索引对查询结果进行排序

你能在办公室里提交一份错误报告吗?也许工程师会说它按设计工作,但在我看来,这种行为是错误的,而且在2.6.4中已经出现了一些查询规划器的问题。如果之前说过,我可能会错过它,但是:null处是否存在已删除的_会影响问题吗



另外,如果你提交了一张罚单,请在你的问题中贴一个链接,或者作为对这个答案的评论,以便其他人可以很容易地跟随。谢谢

您是否对已删除的字段进行了索引?您可以在db.item.find{deleted_at:null}.count上进行测试,以与70043进行比较。我用counts更新了我的问题,为什么您使用sort,而扫描条目的数量应该最大为1,因为您是根据唯一的id进行过滤的?排序部分是使用mongoid ruby gem生成的。如果没有排序部分,它工作得很快。我有一个模型类项include Mongoid::Document include Mongoid::Paranoia self.default\u scoping=->{wheredeleted\u at:nil.asc:positions}end是否对字段deleted\u at进行了索引?您可以在db.item.find{deleted_at:null}.count上进行测试,以与70043进行比较。我用counts更新了我的问题,为什么您使用sort,而扫描条目的数量应该最大为1,因为您是根据唯一的id进行过滤的?排序部分是使用mongoid ruby gem生成的。我有一个模型类Item include Mongoid::Document include Mongoid::Paranoia self.default_scoping=->{wheredeleted_at:nil.asc:positions}end_id在mongodb中是一个特殊的、唯一的索引,类似于关系数据库中的主键。不能有索引存储桶。事实上,据我所知,mongodb中只有复合索引或干草堆索引,没有bucket541fd8016d792e0804820100@na43251,你能在db.items上做个测试吗?在创建你所做的复合索引之前,找到{deleted_at:null,{u id:ObjectId'541fd8016d792e0804820100'}。提示{u id:1}。排序{positions:1}?如果仍然扫描这么多文档,我认为这是一个bug!是的,提示有帮助,我已经降级到2.6.3,看起来问题已经解决了,但我会密切关注这个问题,也许这个问题会在加载后出现。mongodb中的id是一个特殊的、唯一的索引,类似于关系数据库中的主键。不能有索引存储桶。事实上,据我所知,mongodb中只有复合索引或干草堆索引,没有bucket541fd8016d792e0804820100@na43251,你能在db.items上做个测试吗?在创建你所做的复合索引之前,找到{deleted_at:null,{u id:ObjectId'541fd8016d792e0804820100'}。提示{u id:1}。排序{positions:1}?如果仍然扫描这么多文档,我认为这是一个bug!是的,提示有帮助,我已经降级到2.6.3,看起来问题已经解决了,但我会密切关注这个问题,也许这个问题在加载后会出现。我尝试添加id,在索引处删除了_,没有帮助,请参见更新2它真的必须扫描整个集合,而不是使用find筛选的结果集吗?对于以前版本的mongodb,我没有这个问题。关于您的声明,它在运行期间忽略了排序部分。解释操作,您能分享一些官方或可靠的证据吗?谢谢。@na43251,我错误地认为索引中删除的id会有所帮助,请参阅我更新的答案以获得更好的解释。在特定情况下,它会扫描整个集合,而不是过滤集。我认为这是因为查询计划器现在也使用索引交集,无论是出于设计还是作为一个错误,它都以一种反直觉的方式使用索引。@向导,我们观察到同一查询的解释和计划摘要之间存在差异。在解释中,n、nscannedObjects和nscanned都是1。但是,计划摘要显示,在实际查询中,正在扫描70043个对象。另外,如果你看一下,特别是第2段,我们可以得出这样的结论,至少在本例中解释一下,忽略排序将如何影响其使用if索引。我尝试添加id,在索引处删除,没有帮助,请参阅更新2它真的必须扫描整个集合,而不是使用find筛选的结果集吗?对于以前版本的mongodb,我没有这个问题。关于您的声明,它在运行期间忽略了排序部分。解释操作,您能分享一些官方或可靠的证据吗?谢谢。@na43251,我错了
建议在索引处删除一个id将有所帮助,请参阅我的更新答案以获得更好的解释。在特定情况下,它会扫描整个集合,而不是过滤集。我认为这是因为查询计划器现在也使用索引交集,无论是出于设计还是作为一个错误,它都以一种反直觉的方式使用索引。@向导,我们观察到同一查询的解释和计划摘要之间存在差异。在解释中,n、nscannedObjects和nscanned都是1。但是,计划摘要显示,在实际查询中,正在扫描70043个对象。另外,如果你看一看,特别是第2段,我们可以得出这样的结论:至少在本例中,explain忽略了sort如何影响if索引的使用。凭直觉,.explain应该包括.sort。但实际上,手册只是说明它做出了判断。否则,我认为您上面提供的示例无法证明。我的理由是,根据提问者提供的结果,因为索引查询后的文档数量只有一个,所以不需要排序操作。扫描者应该是假的,但它是真的。为什么?我猜.explain只是做出判断,不管查询的最终结果如何。凭直觉,.explain应该包括.sort。但实际上,手册只是说明它做出了判断。否则,我认为您上面提供的示例无法证明。我的理由是,根据提问者提供的结果,因为索引查询后的文档数量只有一个,所以不需要排序操作。扫描者应该是假的,但它是真的。为什么?我猜。解释只是做出判断,不管查询的最终结果如何。