A. Jesse Jiryu Davis —— 10gen工程师,从事MongoDB、Python及Tornado。在Dzone上分享了MongoDB中组合索引的最佳建立方法以及索引中字段的最优顺序。并通过explain()输出的结果来验证实际性能,同时还分析了MongoDB的查询优化器的索引选择机制。
项目背景
预想中的项目是在MongoDB上建立一个类Disqus的评论系统(虽然Disqus使用的是Postgres,但是不影响我们讨论)。这里储存的评论可能是上万条,但是我们先从简单的4条谈起。每条评论都拥有时间戳(timestamp)、匿名(发送)与否(anonymous)以及质量评价(rating)这三个属性:
- { timestamp: 1, anonymous: false, rating: 3 }
- { timestamp: 2, anonymous: false, rating: 5 }
- { timestamp: 3, anonymous: true, rating: 1 }
- { timestamp: 4, anonymous: false, rating: 2 }
这里需要查询的是anonymous = false而且timestamp在2 – 4之间的评论,查询结果通过rating进行排序。我们将分3步完成查询的优化并且通过MongoDB的explain()对索引进行考量。
范围查询
首先从简单的查询开始 —— timestamps范围在2-4的评论:
- > db.comments.find( { timestamp: { $gte: 2, $lte: 4 } } )
查询的结果很显然是3条。然而这里的重点是通过explain()看MongoDB是如何去实现查询的:
- > db.comments.find( { timestamp: { $gte: 2, $lte: 4 } } ).explain()
- {
- "cursor" : "BasicCursor",
- "n" : 3,
- "nscannedObjects" : 4,
- "nscanned" : 4,
- "scanAndOrder" : false
- // ... snipped output ...
- }
先看一下如何读MongoDB的查询计划:首先看cursor的类型。“BasicCursor”可以称得上一个警告标志:它意味着MongoDB将对数据集做一个完全的扫描。当数据集里包含上千万条信息时,这完全是行不通的。所以这里需要在timestamp上加一个索引:
- > db.comments.createIndex( { timestamp: 1 } )
现在再看explain()的输出结果:
- > db.comments.find( { timestamp: { $gte: 2, $lte: 4 } } ).explain()
- {
- "cursor" : "BtreeCursor timestamp_1",
- "n" : 3,
- "nscannedObjects" : 3,