使用limit()和#x2B;时,MongoDB find()查询会扫描文档两次(使用重复的光标);排序()?
我对MongoDB还相当陌生,尽管我还没有找到对我所看到的东西的解释 当我运行以下查询时,我有一个大约200个文档的小数据集:使用limit()和#x2B;时,MongoDB find()查询会扫描文档两次(使用重复的光标);排序()?,mongodb,Mongodb,我对MongoDB还相当陌生,尽管我还没有找到对我所看到的东西的解释 当我运行以下查询时,我有一个大约200个文档的小数据集: db.tweets.find({user:22438186}) 我在9得到n/nscannedObjects/nscanned/nscannedObjectsAllPlans/nscannedallplan。光标为BtreeCursor用户_1。一切都好 介绍排序() 如果我将排序附加到查询: db.tweets.find({user:22438186}).sort(
db.tweets.find({user:22438186})
我在9得到n/nscannedObjects/nscanned/nscannedObjectsAllPlans/nscannedallplan。光标为BtreeCursor用户_1。一切都好
介绍排序() 如果我将排序附加到查询:
db.tweets.find({user:22438186}).sort({created\u at:1})
nscannedObjectsAllPlans/nscannedAllPlans已增至30。我可以在allPlans字段下看到:
BtreeCursor在\u 1处创建\u扫描了21个文档并匹配了2个?我不确定这里发生了什么,因为我认为sort()
应用于find()
返回的文档,它似乎是用户索引中的9。在写这篇文章的过程中,我从allPlans字段收集到,出于某种原因,它也在使用我创建的_at_1索引
限制(>n)与Sort()组合==重复光标和文档扫描? 当我追加
limit(10)
或更高时,n保持在9,nscannedObjects/nscanned都在18并且nscannedObjectsAllPlans/nscannedAllPlans现在返回60为什么除n以外的所有对象都加倍了?光标现在是QueryOptimizerCursor,myexplain(true)
结果中有一个子句字段,两个子对象完全相同,相同的光标使用了两次,导致了重复?这种行为正常吗
{
"cursor" : "BtreeCursor user_1",
"isMultiKey" : false,
"n" : 9,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
]
}
}
我尝试了几个不同的限制值,注意到使用9的限制时,nscannedObjects/nscanned都返回到9的值,nscannedObjects/nscannedAllPlans下降到29,随着限制的减小,下降了1
但是,在子句下,第二个子对象与限制查询10及以上的查询不同。光标字段现在显示BtreeCursor省略用户_1出于某种原因,所有n个字段的值均为0,而不是9,此外,对象的其余部分是相同的。对于所有这些限制查询,allPlans字段列出了子句字段和在_1处创建的BtreeCursor的另一个字段(用作限制为1的查询的光标)
实际问题 那么,当
find()
中同时使用limit()
和sort()
时,究竟是什么原因导致我的文档被扫描两次呢?只有当限制超过nscannedObjects或nscanned时,问题才会出现。仅使用limit()
或sort()
进行查询时,不会扫描两次文档
更新
抱歉造成混淆,第一个代码块在allplan字段下显示光标数据。实际使用的光标是*BtreeCursor user\u 1*
第二个代码块来自具有limit()
和sort()
的查询。我提供了子句下列出的游标数据,子句字段列出了相同的游标信息两次(重复)。该查询的实际光标字段是*QueryOptimizerCursor*
。子句下的重复游标是*BtreeCursor user\u 1*
此后,我添加了一个复合索引{user:1,创建了_at:1},n个字段的结果是9,18。无论limit()
值或使用sort()
。出于某种原因,在allPlans下,我的原始用户id 1索引仍然与新的复合索引一起运行。如果对查询应用了限制,而不是使用索引用户\u id\u 1/BtreeCursor用户\u 1,则将使用带有子句中两个游标的QueryOptimizerCursor
我一直在进一步研究这个问题,似乎查询计划器并行使用其他索引并选择最佳索引结果?我不确定是否每次执行此查询时都会再次发生此“竞争”,或者是否缓存了它
db.tweets.find({user:22438186}).sort({created_at:1}).limit(10)
在不使用复合索引的情况下运行查询会产生以下结果:
{
"clauses" : [
{
"cursor" : "BtreeCursor user_1",
"isMultiKey" : false,
"n" : 9,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
]
}
},
{
"cursor" : "BtreeCursor user_1",
"isMultiKey" : false,
"n" : 9,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
]
}
}
],
"cursor" : "QueryOptimizerCursor",
"n" : 9,
"nscannedObjects" : 18,
"nscanned" : 18,
"nscannedObjectsAllPlans" : 60,
"nscannedAllPlans" : 60,
"scanAndOrder" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"allPlans" : [
{
"clauses" : [
{
"cursor" : "BtreeCursor user_1",
"isMultiKey" : false,
"n" : 9,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
]
}
},
{
"cursor" : "BtreeCursor user_1",
"isMultiKey" : false,
"n" : 9,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
]
}
}
],
"cursor" : "QueryOptimizerCursor",
"n" : 9,
"nscannedObjects" : 18,
"nscanned" : 18,
"scanAndOrder" : false,
"nChunkSkips" : 0
},
{
"cursor" : "BtreeCursor created_at_1",
"isMultiKey" : false,
"n" : 3,
"nscannedObjects" : 42,
"nscanned" : 42,
"scanAndOrder" : false,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"created_at" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
}
}
],
"server" : "HOME-PC:27017",
"filterSet" : false,
"stats" : {
"type" : "KEEP_MUTATIONS",
"works" : 43,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 32,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
{
"type" : "OR",
"works" : 42,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 32,
"needFetch" : 0,
"isEOF" : 1,
"dupsTested" : 18,
"dupsDropped" : 9,
"locsForgotten" : 0,
"matchTested_0" : 0,
"matchTested_1" : 0,
"children" : [
{
"type" : "SORT",
"works" : 21,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 10,
"needFetch" : 0,
"isEOF" : 1,
"forcedFetches" : 0,
"memUsage" : 6273,
"memLimit" : 33554432,
"children" : [
{
"type" : "FETCH",
"works" : 10,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 0,
"children" : [
{
"type" : "IXSCAN",
"works" : 10,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : "{ user: 1 }",
"isMultiKey" : 0,
"boundsVerbose" : "field #0['user']: [22438186.0, 22438186.0]",
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 9,
"children" : []
}
]
}
]
},
{
"type" : "SORT",
"works" : 21,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 10,
"needFetch" : 0,
"isEOF" : 1,
"forcedFetches" : 0,
"memUsage" : 6273,
"memLimit" : 33554432,
"children" : [
{
"type" : "FETCH",
"works" : 10,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 0,
"children" : [
{
"type" : "IXSCAN",
"works" : 10,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : "{ user: 1 }",
"isMultiKey" : 0,
"boundsVerbose" : "field #0['user']: [22438186.0, 22438186.0]",
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 9,
"children" : []
}
]
}
]
}
]
}
]
}
}
使用复合索引:
{
"cursor" : "BtreeCursor user_1_created_at_1",
"isMultiKey" : false,
"n" : 9,
"nscannedObjects" : 9,
"nscanned" : 9,
"nscannedObjectsAllPlans" : 18,
"nscannedAllPlans" : 18,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
],
"created_at" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
},
"allPlans" : [
{
"cursor" : "BtreeCursor user_1_created_at_1",
"isMultiKey" : false,
"n" : 9,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : false,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
],
"created_at" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
}
},
{
"clauses" : [
{
"cursor" : "BtreeCursor user_1",
"isMultiKey" : false,
"n" : 0,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
]
}
},
{
"cursor" : "BtreeCursor ",
"isMultiKey" : false,
"n" : 0,
"nscannedObjects" : 0,
"nscanned" : 0,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"user" : [
[
22438186,
22438186
]
]
}
}
],
"cursor" : "QueryOptimizerCursor",
"n" : 0,
"nscannedObjects" : 9,
"nscanned" : 9,
"scanAndOrder" : false,
"nChunkSkips" : 0
}
],
"server" : "HOME-PC:27017",
"filterSet" : false,
"stats" : {
"type" : "LIMIT",
"works" : 11,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
{
"type" : "FETCH",
"works" : 11,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 0,
"children" : [
{
"type" : "IXSCAN",
"works" : 10,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 9,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : "{ user: 1, created_at: 1 }",
"isMultiKey" : 0,
"boundsVerbose" : "field #0['user']: [22438186.0, 22438186.0], field #1['created_at']: [MinKey, MaxKey]",
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 9,
"children" : []
}
]
}
]
}
}
希望这能消除困惑。如果您看到
explain()
计划,您可以看到:
db.tweets.find({user:22438186})
使用user_1
索引
db.tweets.find({user:22438186}).sort({created_at:1})
使用created_at_1
索引
这表明mongodb选择了created_at_1
而不是user_1
,因为排序操作在使用索引时性能更好,并且排序操作基于created_at
字段。这使得mongodb
忽略user\u 1
索引并执行完全收集扫描
因此,我们需要在这些情况下仔细定义我们的目标。如果我们在user\u 1
和created\u at\u 1
上都有一个复合索引,则不会进行全表扫描,mongodb将选择同时支持find
和sort
操作的索引,在这种情况下,该索引将是复合索引
对mongoDB为什么使用QueryOptimizerCursor
光标有一个很好的解释
nscannedObjectsAllPlans/nscannedAllPlans下降到29
您不必担心这两个参数,它们是mongodb为选择适当索引而执行的所有计划的组合扫描的表示
nscannedObjectsAllPlans是一个反映
在数据库操作期间扫描所有查询计划的文档
NSCanendallPlans是一个反映
在查询过程中扫描所有查询计划的文档或索引项
数据库操作
这些线是从
那么,当limit()和sort()都在一个应用程序中使用时,究竟是什么原因导致我的文档被扫描两次呢
db.tweets.find({user:22438186})