MongoDB 索引 二

一: 如何选择索引字段

假定我们查询语句为:

db.users.find().sort({"age":1,"username":1})

这里根据age排序再根据usename排序,上一章节建立的针对username的索引已经不起作用了。所以我们这里需要建立

复合索引(compound index),简单说来就是一个建立在多字段上的索引。

执行

db.users.ensureIndex({"age":1,"username":1})

那么这个索引大致会是这样子的:

 [0,"user0001"]  --> 0x0asdfs

 [0,"user0002"]  --> 0x0asddd

  ...

 [23,"user0891"]  --> 1x2xxdfs

...

每个索引条目都包含一个“age”字段和一个“username”字段,并且指向文档在磁盘中的存储位置。

MongoDB对这个索引的使用方式取决于查询的类型。

  • db.users.find({"age":21}).sort({"username":-1})

    这是一个点查询,用户查找单个值。由于索引中又username,所以查询结果已经是有序的。注意:排序方向不重要,MongoDB可以在任意方向上对索引进行遍历。

    这种类型的排序是非常高效的,能够直接定位到年龄,而且不需要对结果进行排序。

  • db.users.find({"age":{"$gte":21,"$lte":30}})

    这是一个多值查询,查找到多个值相匹配的文档。

  •   db.users.find({"age":{"$gte":21,"$lte":30}}).sort({"username":1})

    这是一个多值查询,与上一个查询类似,只是需要对结果在内存中进行排序,所以效率通常不如上一个。

注意:如果结果集的大小超过32M,MongoDB排序时候就会出错。

  如果对于上一个查询,使用另一个索引{"username":1,"age":1}.

db.users.find({"age":{"$gte":21,"$lte":30}}).sort({"username":1}).hint({"username":1,"age":1}).explain()

虽然这个时候查找age需要对全部索引进行扫描挑选出部分匹配的文档,但是却不需要在内存中进行排序。

因此,如果对查询结果的范围做了限制,那么MongoDB在几次匹配之后就不再扫描索引,在这种情况下,将排序键放在第一位是一个非常好的策略。     

对比两个索引效率

> db.users.find({"age":{"$gte":21,"$lte":30}}).sort({"username":1}).hint({"age":1,"username":1}).explain()
{
	"cursor" : "BtreeCursor age_1_username_1",
	"isMultiKey" : false,
	"n" : 795,
        ...
	"scanAndOrder" : true,
	"indexOnly" : false,
	"nYields" : 0,
	"nChunkSkips" : 0,
	"millis" : 6,

}
> db.users.find({"age":{"$gte":21,"$lte":30}}).sort({"username":1}).hint({"username":1,"age":1}).explain()
{
	"cursor" : "BtreeCursor username_1_age_1",
	"isMultiKey" : false,
	"n" : 795,
	"nscannedObjects" : 795,
	"nscanned" : 9834,
	...
	"scanAndOrder" : false,
	"indexOnly" : false,
	
	"millis" : 27,
}

对比鲜明,第二个速度还是不如第一个快。但是如果我们限制每次查询的结果数目,第二个速度就比较快了。

db.users.find({"age":{"$gte":21,"$lte":30}}).limit(10).sort({"username":1}).hint({"age":1,"username":1}).explain()
{
	"cursor" : "BtreeCursor age_1_username_1",
	"isMultiKey" : false,
	"n" : 10,
	"nscannedObjects" : 795,
	"nscanned" : 795,
        ...
	"scanAndOrder" : true,
	"indexOnly" : false,
	"nYields" : 0,
	"nChunkSkips" : 0,
	"millis" : 5,

}
>

 

> db.users.find({"age":{"$gte":21,"$lte":30}}).limit(10).sort({"username":1}).hint({"username":1,"age":1}).explain()
{
	"cursor" : "BtreeCursor username_1_age_1",
	"isMultiKey" : false,
	"n" : 10,
	"nscannedObjects" : 10,
	"nscanned" : 139,
        ...
	"scanAndOrder" : false,
	"indexOnly" : false,
	"nYields" : 0,
	"nChunkSkips" : 0,
	"millis" : 0,

}

hint: 强制查询使用某个索引

explain : 查看查询详细数据,包含使用索引以及扫描的文档数目,是否排序等等。

二:索引类型

  1. 复合索引

  2. 覆盖索引 (covered index)

    如果查询只需要查找索引中包含的字段,那就没有必要获取实际的文档。为了确保查询只需要索引就可以完成,应该使用投射。

  3. 隐式索引

    如果一个拥有N个键值的索引,那么你同时拥有了所有这N个键的前缀组成的索引。

你可能感兴趣的:(MongoDB 索引 二)