这个 .explain() 中额外的 "nscanned" 是从哪里来的?

Where does the extra "nscanned" comes from in this .explain()?

我有一些预订。每个文档都有这些字段:

_id: ObjectID, 
client: ObjectID // A reference to the "owner" of the service. 
start: nr, 
end nr

我插入了 15 个虚拟文档(在客户端上具有相同的 ID)。前五个从 1 开始到 2 结束,接下来的五个从 3 开始到 4 结束,最后五个从 5 开始到结束在 6

然后我创建以下索引:db.bookings.ensureIndex({client: 1, start: 1, end: 1})

对于这些查询:

db.bookings.find({client: anID, start: {$gte: 1}}).explain()
db.bookings.find({client: anID, start: {$gte: 3}}).explain()
db.bookings.find({client: anID, start: {$gte: 5}}).explain()

我按顺序得到了这些预期结果:

"n": 15, "nscannedObjects": 15, "scanned": 15
"n": 10, "nscannedObjects": 10, "scanned": 10
"n": 5, "nscannedObjects": 5, "scanned": 5

这是意料之中的,完全没问题。

但是在执行这些查询时:

db.bookings.find({client: anID, start: {$gte: 1}, end: {$lte: 2}}).explain()
db.bookings.find({client: anID, start: {$gte: 3}, end: {$lte: 4}}).explain()
db.bookings.find({client: anID, start: {$gte: 5}, end: {$lte: 6}}).explain()

我按顺序得到了这些令人困惑的结果:

"n": 5, "nscannedObjects": 5, "scanned": 7
"n": 5, "nscannedObjects": 5, "scanned": 6
"n": 5, "nscannedObjects": 5, "scanned": 5

为什么我在前两个查询中得到额外的(预计 5 个)扫描文件?我的索引或查询有问题吗?

nscanned 计数可能高于 nscannedObjects 计数,因为 MongoDB 为特定 object/document 扫描了多个索引条目,然后进行了重复数据删除。这实际上是文档中 explain example 的一部分。对于复合键,也可能是索引条目被扫描的情况,因为它在最左边的字段上匹配(例如),但因为它在其他字段之一上不匹配,所以文档被跳过而不被扫描。

有一个 excellent post here 具有 btree 遍历的视觉效果,以帮助理解这是如何发生的。

顺便说一句,3.0 版的新版本explain 会将这些字段替换为totalKeysExaminedtotalDocsExamined。它们代表相同的东西,但名称更能说明它们实际代表的内容。

在查询两个没有关系的范围的情况下,就像我上面做的那样。 (开始和结束)我发现 this great tip 关于 "closing" 查询中的第一个范围。如果第一个范围是封闭的,查询的性能可能会好得多。