MongoDB:即使查询的所有字段都被索引,为啥我的扫描对象值很高?
Posted
技术标签:
【中文标题】MongoDB:即使查询的所有字段都被索引,为啥我的扫描对象值很高?【英文标题】:MongoDB: Why is my scannedObjects value high even all fields of the query are indexed?MongoDB:即使查询的所有字段都被索引,为什么我的扫描对象值很高? 【发布时间】:2015-01-28 20:36:58 【问题描述】:我正在索引一个集合中的三个字段,其中一个是数组。我正在对这三个字段运行查询,并且查询需要一秒钟以上,集合中有 300K 字段。当我在查询上调用解释时,我看到我的索引被正确使用,但扫描对象的数量非常高。我想这就是性能低下的原因。
"_id" : ObjectId("54c8f110389a46153866d82e"),
"mmt" : [
"54944cfd90671810ccbf2552",
"54c64029038d8c3aff41ad6d",
"54c64029038d8c3aff41ad73",
"54c8f151038d8c3aff453669",
"54c8f151038d8c3aff45366d"
],
"p" : 8700,
"sui" : "3810d5cf-3032-4a77-9715-a42e010e569c"
/* also some more fields */
有了这个索引:
"sui" : 1,
"p" : 1,
"mmt" : 1
我正在尝试运行此查询:
db.my_coll.find(
"mmt" : "$all" :
[
"54944cfd90671810ccbf2552", "54ac1db0e3f494afd4ded4c8", "54ac1db1e3f494afd4ded66a", "54ac1db1e3f494afd4ded66b", "54c8b671038d8c3aff453649", "54c8f154038d8c3aff45368f", "54c8f154038d8c3aff453694"
]
,
"sui" : "$ne" : "bde0f517-b942-4823-b2c8-a41900f46641" ,
"p": $gt: 100, $lt: 1000
).limit(1000).explain()
解释的结果是:
"cursor" : "BtreeCursor sui_1_p_1_mmt_1",
"isMultiKey" : true,
"n" : 16,
"nscannedObjects" : 14356,
"nscanned" : 129223,
"nscannedObjectsAllPlans" : 14356,
"nscannedAllPlans" : 129223,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 1009,
"nChunkSkips" : 0,
"millis" : 1276,
"indexBounds" :
"sui" : [
[
"$minElement" : 1
,
"bde0f517-b942-4823-b2c8-a41900f46641"
],
[
"bde0f517-b942-4823-b2c8-a41900f46641",
"$maxElement" : 1
]
],
"p" : [
[
-Infinity,
1000
]
],
"mmt" : [
[
"54944cfd90671810ccbf2552",
"54944cfd90671810ccbf2552"
]
]
,
"server" : "shopkrowdMongo:27017",
"filterSet" : false,
"stats" :
"type" : "LIMIT",
"works" : 129224,
"yields" : 1009,
"unyields" : 1009,
"invalidates" : 0,
"advanced" : 16,
"needTime" : 129207,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
"type" : "KEEP_MUTATIONS",
"works" : 129224,
"yields" : 1009,
"unyields" : 1009,
"invalidates" : 0,
"advanced" : 16,
"needTime" : 129207,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
"type" : "FETCH",
"works" : 129224,
"yields" : 1009,
"unyields" : 1009,
"invalidates" : 0,
"advanced" : 16,
"needTime" : 129207,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 16,
"children" : [
"type" : "IXSCAN",
"works" : 129223,
"yields" : 1009,
"unyields" : 1009,
"invalidates" : 0,
"advanced" : 14356,
"needTime" : 114867,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : " sui: 1.0, p: 1.0, mmt: 1.0 ",
"isMultiKey" : 1,
"boundsVerbose" : "field #0['sui']: [MinKey, \"bde0f517-b942-4823-b2c8-a41900f46641\"), (\"bde0f517-b942-4823-b2c8-a41900f46641\", MaxKey], field #1['p']: [-inf.0, 1000.0), field #2['mmt']: [\"54944cfd90671810ccbf2552\", \"54944cfd90671810ccbf2552\"]",
"yieldMovedCursor" : 0,
"dupsTested" : 14356,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 129223,
"children" : []
]
]
]
找到的项目数是16,但scannedObjects的数量是14356。我不明白为什么mongodb扫描这么多文档,即使查询的所有字段都被索引了。
-
mongodb 为什么要扫描这么多对象?
如何更快地获得此查询的结果?
我使用的 mmt 数组不会随着时间的推移而增长或缩小,但其中的元素数量在 5 到 15 之间变化。我需要使用 $in、$all 和 $nin 的几种组合来查询该字段。此集合中的项目数量可能会增长超过 3000 万。有没有办法可靠地快速获得这种情况下的结果?
更新 1:
我尝试删除 sui 字段和 $ne 查询。更新说明:
"cursor" : "BtreeCursor p_1_mmt_1",
"isMultiKey" : true,
"n" : 17,
"nscannedObjects" : 16338,
"nscanned" : 16963,
"nscannedObjectsAllPlans" : 16338,
"nscannedAllPlans" : 33930,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 265,
"nChunkSkips" : 0,
"millis" : 230,
"indexBounds" :
"p" : [
[
-Infinity,
1000
]
],
"mmt" : [
[
"54944cfd90671810ccbf2552",
"54944cfd90671810ccbf2552"
]
]
,
"server" : "shopkrowdMongo:27017",
"filterSet" : false,
"stats" :
"type" : "LIMIT",
"works" : 16966,
"yields" : 265,
"unyields" : 265,
"invalidates" : 0,
"advanced" : 17,
"needTime" : 16947,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
"type" : "KEEP_MUTATIONS",
"works" : 16966,
"yields" : 265,
"unyields" : 265,
"invalidates" : 0,
"advanced" : 17,
"needTime" : 16947,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
"type" : "FETCH",
"works" : 16965,
"yields" : 265,
"unyields" : 265,
"invalidates" : 0,
"advanced" : 17,
"needTime" : 16947,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 17,
"children" : [
"type" : "IXSCAN",
"works" : 16964,
"yields" : 265,
"unyields" : 265,
"invalidates" : 0,
"advanced" : 16338,
"needTime" : 626,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : " p: 1.0, mmt: 1.0 ",
"isMultiKey" : 1,
"boundsVerbose" : "field #0['p']: [-inf.0, 1000.0), field #1['mmt']: [\"54944cfd90671810ccbf2552\", \"54944cfd90671810ccbf2552\"]",
"yieldMovedCursor" : 0,
"dupsTested" : 16338,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 16963,
"children" : []
]
]
]
查询执行得更好,但scannedObjects仍然很高。
【问题讨论】:
记住扫描的对象只不过是一个计数器,因此如果您扫描一个子文档,它将远高于返回的数量 【参考方案1】:我认为 marcinn 将 $ne
列为最可能的罪魁祸首是正确的,但更新 1 向我们展示了 $all
也是一个问题。该查询使用索引的mmt
部分来查找包含数组中的值之一的文档,然后必须扫描mmt
数组的其余部分以验证$all
数组中的所有值是否在可能匹配的文档的mmt
数组。这意味着必须加载和扫描可能匹配的文档,因此它被视为已扫描对象。为了非常清楚地展示这种行为,请考虑以下示例:
> db.test.drop()
> for (var i = 0; i < 100; i++) db.test.insert( "x" : [1, 2] )
> for (var i = 0; i < 100; i++) db.test.insert( "x" : [1, 3] )
> db.test.ensureIndex( "x" : 1 )
> db.test.find( "x" : "$all" : [1, 2] ).explain(true)
这显示了 n = 100
和 nscanned = nscannedObjects = 200
使用值 1 作为两个索引边界的结果,而逻辑上等效的查询
> db.test.find( "x" : "$all" : [2, 1] ).explain(true)
显示n = nscanned = nscannedObjects = 100
,两个索引边界的值都为 2。
【讨论】:
这很有帮助,谢谢。您能否提出一种以可扩展的方式解决此问题的方法?$ne
没有选择性,不能很好地扩展。您使用的是哪个版本的 MongoDB? $all
的性能故障已在 SERVER-1000 中得到解决。解决方法是通过索引交集来处理这种情况,这是 MongoDB >= 2.6 的一个特性。可能值得重新考虑架构设计,以便能够编写更具扩展性的查询。如果您愿意,您应该提出一个单独的问题,解释您的文档结构和查询背后的用例,我们将对其进行研究。【参考方案2】:
基本上这是因为 $ne 不能(有效地)使用索引。所以你的索引被使用只是因为你首先通过 mnt 字段查询然后它的阅读
某些查询操作不是选择性的。这些操作不能使用 索引有效或根本不能使用索引。
不等式运算符 $nin 和 $ne 的选择性不是很高,因为它们 经常匹配索引的很大一部分。结果,在大多数情况下, 带有索引的 $nin 或 $ne 查询可能不会比 $nin 或 $ne 必须扫描集合中所有文档的查询
http://docs.mongodb.org/manual/core/query-optimization/
【讨论】:
我删除了 sui。查询执行得更好,但scannedObjects 仍然很高。我在问题中加入了新的解释。以上是关于MongoDB:即使查询的所有字段都被索引,为啥我的扫描对象值很高?的主要内容,如果未能解决你的问题,请参考以下文章