我们知道MongoDB的索引是B-Tree结构,和MySQL的索引非常相似。所以你应该听过这样的建议:在创建索引时,你应该考虑排序操作,并尽量将排序操作使用的字段放在你的索引后面。但在某些情况下,这样做会使你的查询性能降低。例如,我们执行如下查询:db.collection.find({"country":"A"}).sort({"carsOwned":1})查询条件为{"country":"A"},在carsOwned字段上按升序排序。所以索引的构建很简单,直接创建country和carsOwned这两个字段的联合索引即可。像这样:db.collection.ensureIndex({"country":1,"carsOwned":1})让我们看一个稍微复杂一点的查询:db.collection.find({"country":{"$in":["A","G"]}}).sort({"carsOwned":1})这次我们要查询国家为A或G的数据条目,结果也是按carsOwned字段排序。如果我们仍然使用上面的索引,使用explain()来分析查询,会发现输出中有一个"scanAndOrder":true字段,而nscanned的值可能比预期的要大很多,即使指定了limit也没有效果。这是什么原因?我们先看下图:如上图所示,左边是按照{“country”:1,“carsOwned”:1}的顺序建立的索引。右边是按照{"carsOwned":1,"country":1}的顺序建立的索引。如果我们执行上面的查询,通过左边的索引,我们需要取所有国家值为A的子节点(左图左边分支)和所有国家值为G的子节点(左图右边分支))也来了。然后根据carsOwned的值对检索到的数据进行排序操作。所以上面的explain输出了“scanAndOrder”:true的提示,意思是本次查询是先通过scan获取数据,再进行独立的排序操作。那么如果我们使用右边的索引来做查询,结果就不一样了。我们没有把sort字段放在***上,而是放在前面,而是把filter字段放在后面。这样做的结果是:我们会从值为1的节点开始遍历(右图左边的分支),找到值为A或者G的国家就直接放入结果中放。当指定数量(指定限制数量)的搜索完成时。我们可以直接返回结果,因为此时所有的结果本身都是按照carOwned的正序排列的。对于上面的数据集,如果我们需要2个结果。我们需要通过左边的索引扫描4条记录,然后对这4条记录进行排序,返回结果。右边我们只需要扫描2个结果就可以直接返回(因为查询过程就是按照要求的顺序遍历索引)。因此,当有范围查询时(包括$in、$gt、$lt等),刻意在末尾加一个排序索引通常是没有效果的。因为在范围查询的过程中,我们得到的结果集并不是按照添加的字段来排列的,需要额外进行一次排序。这种情况下,倒序建索引(排序字段在前,范围查询字段在后)可能是更好的选择。当然,是否更好也和具体的数据集有关。总结一下,举两个栗子。当查询为:db.test.find({a:1,b:2}).sort({c:1})时,则直接创建{a:1,b:1,c:1}或{b:1,a:1,c:1}联合索引就足够了。如果查询是:db.test.find({a:1,b:{$in:[1,2]}}).sort({c:1})那么就可以构建{a:1,c:1,b:1}的联合索引会更合适。当然这里只是另一种思路,要不要用还是要看你的数据情况。原文链接:http://blog.nosqlfan.com/html/4117.html【编辑推荐】先睹为快:OracleNoSQLDatabase八大主流NoSQL数据库系统对比解读NoSQL数据库四大家族NoSQL在企业中的发展史一家创业公司是否适合做NoSQL的探讨
