我有一个带有以下索引的MongoDB上限集合:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "events.messageIn"
},
{
"v" : 1,
"key" : {
"uuid" : 1,
"ts" : -1
},
"name" : "uuid_1_ts_-1",
"ns" : "events.messageIn",
"background" : true
}
]
正如您可能推断的那样,这是一组事件数据。由于时间戳始终在增加,因此订单{$natural: -1}
应与订单{ts: -1}
等效。我偶然在ts
添加了一个索引,以便能够对时间片进行有效查询(例如db.messageIn.find({ts: {$gte: ISODate("2016-08-01")}})
然而,我(或许是天真地)期望db.messageIn.find({uuid: SOME_UUID}).sort({$natural: -1})
能够最有效地回答逻辑问题“给我一些最新的消息”。实际上,该查询是一个集合扫描,比db.event.find({uuid: SOME_UUID}).sort({ts: -1})
的计划慢几个数量级。
为什么会这样?推测性地,我假设这是因为MongoDB认为,一旦它在查看索引,它将无法提供自然顺序;结果,它回归到集合扫描。对于这个查询,MongoDB是否有一些方法可以更聪明?例如,使用书籍索引的类比:
uuid ts page
abcdef 2016-06-01T00:03 10
abcdef 2016-06-01T00:02 8
abcdef 2016-06-01T00:01 6
ghijkl 2016-06-01T00:03 9
mnopqr 2016-06-01T00:02 7
mnopqr 2016-06-01T00:01 5
按uuid
过滤后,您可以轻松按页面排序;没有必要按顺序访问每个页面,检查其uuid,并在页面匹配时生成页面。 MongoDB的“页面”引用是否以某种方式不可订购?很明显,我的心理模型在某处不足。
答案 0 :(得分:0)
根据我对the documentation的理解,支持更高的插入吞吐量上限集合,不要使用索引按插入顺序(或向后插入顺序)返回文档。因此,当执行db.messageIn.find({uuid: SOME_UUID}).sort({$natural: -1})
时,将逐个检查每个文档以保留插入顺序并过滤匹配文档。
另一方面,db.event.find({uuid: SOME_UUID}).sort({ts: -1})
要快得多,因为它使用" uuid_1_ts_-1"匹配uuid和排序的索引。在MongoDB中,排序操作可以通过根据索引中的排序检索文档来获取排序顺序(documentation描述的案例与您的案例相似)。
顺便说一下," uuid_1_ts_-1"不会在db.messageIn.find({ts: {$gte: ISODate("2016-08-01")}})
之类的查询中使用,因为它是复合索引,对于复合索引,MongoDB只能使用the index prefixes来支持查询。