我正在评估以下查询的性能。
db.products_old.find({ regularPrice: { $lte: 200 } })
该集合有超过一百万个文档,总共约 0.15GB。
无索引
这是预料之中的。必须进行全列扫描
"executionTimeMillis" : 1019,
<子>子>
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"regularPrice" : {
"$lte" : 200
}
},
"direction" : "forward"
},
索引{ regularPrice: 1 }
"executionTimeMillis" : 2842,
<子>子>
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"regularPrice" : 1
},
"indexName" : "regularPrice_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"regularPrice" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"regularPrice" : [
"[-inf.0, 200.0]"
]
}
}
},
现在它使用了索引,但是执行时间明显变差了。 为什么?
此外,如果性能更差,为什么 Mongo 不使用 COLLSCAN
而 使用会减慢执行速度的索引? rejectedPlans
为空,这表明甚至没有考虑其他计划。为什么?
Here's完整的 allPlansExecution
输出。
最佳答案
在执行COLLSCAN
时,MongoDB 正在从存储驱动器中读取并将匹配的文档存储在RAM 中以备后用。另一方面,IXSCAN
读取存储索引数据的索引和指向它们在存储驱动器上的位置的指针。 (Here's a nice visualisation 从幻灯片 6 到幻灯片 20 左右)
您的集合中有很多文档,但您的索引中也有很多匹配的文档。存储在存储驱动器上的数据并没有以最好的方式存储(就像在索引中一样),所以当 IXSCAN
返回指向它为您的查询找到的 220k+ 文档的指针时, FETCH
需要以随机访问方式从存储驱动器读取 220k+ 次。这很慢。另一方面,我假设 COLLSCAN
正在执行顺序读取,这可能是逐页完成的,并且比 FETCH
读取快得多。
所以总结一下:不是索引让你慢下来,而是 FETCH
阶段。如果你仍然想使用这个索引并有一个更快的查询执行时间,然后使用 .select('-_id regularPrice')
这将只是添加一个快速的 PROJECTION
阶段和从索引中读取所有必需的字段。或者,如果您需要 _id
,则添加索引 {regularPrice: 1, _id: 1}
。
关于 为什么 Mongo 使用索引的部分,即使它知道集合扫描更快:好吧,我认为如果它看到索引,它就会使用它。但是你可以force it to use collection scan通过使用传递给它的 {natural: 1}
的 hint
方法。
关于mongodb - 为什么添加索引会降低性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52243916/