我正在评估以下查询的性能。
db.products_old.find({ regularPrice: { $lte: 200 } })
该馆藏的文档超过一百万,总计约0.15GB。
这是预期的。必须进行全列扫描
"executionTimeMillis" : 1019,
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"regularPrice" : {
"$lte" : 200
}
},
"direction" : "forward"
},
"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
输出。
答案 0 :(得分:1)
在进行COLLSCAN
时,MongoDB正在从存储驱动器读取并将匹配的文档存储在RAM中,以备后用。另一方面,IXSCAN
读取存储索引数据的索引和指向它们在存储驱动器上位置的指针。 (Here's a nice visualisation从幻灯片6到幻灯片20左右)
您的集合中有很多文档,但是索引中也有很多匹配的文档。存储在存储驱动器上的数据并不是以最佳方式存储(从索引中读取),因此,IXSCAN
返回指向您查询的220k +文档的指针时,{{1 }}需要以随机访问方式从存储驱动器读取220k +次。太慢了另一方面,我假设FETCH
正在执行顺序读取,这可能是逐页进行的,并且比COLLSCAN
读取要快得多。
所以总结一下:不是索引使您减速,而是FETCH
阶段。如果您仍想使用此索引并具有更快的查询执行时间,请使用FETCH
,它只会添加一个快速的.select('-_id regularPrice')
阶段并从索引中读取所有必需的字段。或者,如果您需要PROJECTION
,则添加一个索引_id
。
关于为什么这部分,Mongo是否使用索引,即使它可以知道集合扫描更快:好吧,我认为,如果看到索引,它将使用它。但是您可以通过将{regularPrice: 1, _id: 1}
传递给hint
来使用force it to use collection scan。