`

MongoDB索引知识点的整理

阅读更多

 

链接地址: http://quentinXXZ.iteye.com/blog/2125433

内容主要来自《 MongoDB The Definitive Guide 2nd Edition》

最基本的建索引命令如下:

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

根据username建立索引。1表示升序存储。

MonogDB每个一个collection最多可建64个索引。

 

利用hint可指定使用哪个索引。

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

... sort({"username" : 1}).

... hint({"username" : 1, "age" : 1}).

采用.hint({"username" : 1, "age" : 1})扫描量变大,但排序变快

 

组合索引

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

... sort({"username" : 1}).

... limit(1000).

... hint({"age" : 1, "username" : 1}).

... explain()['millis']

用时2031 ms

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

... sort({"username" : 1}).

... limit(1000).

... hint({"username" : 1, "age" : 1}).

... explain()['millis']

用时181ms

所以官方建议使用{"sortKey" : 1, "queryCriteria" : 1}的索引方式定义组合索引,即将排序条件放在前,查询条件字段放在后,因为大多数应用并不要求返回所有符合结果,只要满足条件的结果就够了(大多实际应用都会使用limit)。

索引的升降序方向只在用需要根据多键进行排序才有意义。{"age" : 1}的索引,对{"age" : -1}或者{"age" : 1}的排序来说性能是一样的。

                                                     

索引覆盖(covered indexes

与关系型数据的索引索引覆盖的概念基本相同,可以避免回表查询。

注意的是如果你的索引字段是包含数组的,那么该索引将无法做到索引覆盖,这与数组在索引中的具体存储方式有关。即使你的查询结果不需要该数组字段,你依然无法做到索引覆盖。

 

$操作符如何使用索引

无法使用索引的情况:

1、当使用“ $where"进行查询时,是用不到索引的。

2、检测Document的某个键是否存在( {"key" : {"$exists" : true}} ),也是无法使用索引的。因为对于索引来说不存在与null这两种状态的表示是相同的。

                                                                                                              

官方建议:将用于精确查询的字段放在索引前面,用于范围查询的字段放索引后面。

 

注意,如下查询

db.foo.find({"$or" : [{"x" : 123}, {"y" : 456}]}) 如果x与y单独索引都有,则两个索引都会用到,分别查询,再去重。P93

所以尽可能使用in,不要使用or.

 

索引内嵌文档与数组

以下一个嵌套文档的例子

{

"username" : "sid",

"loc" : {

"ip" : "1.2.3.4",

"city" : "Springfield",

"state" : "NY"

}

对这样的文档,我们可以只对city建立索引db.users.ensureIndex({"loc.city" : 1}),也可以对loc建立索引db.users.ensureIndex({"loc" : 1})。

注意,对整个子文档loc建索引只会对完全匹配整个子文档的查询有帮助。

 

Index on array

对数组字段建索引,其实是对数组的每一个元素建索引,所以如果一篇post有20条comment,就会有20条索引项。这使得对索引的维护代价变得更加的昂贵。还有对数组原数的索引,并不会保存这些元素的位置信息,所以你无法使用通过索引来实现类似“comments.4”这种方式的定位。

 

Index Cardinality(区分度)

例如性别,不应用作索引,因为它的index cardinality(区分度)太低。利用索引区分出找到特定性名的项后,还要去回表查询近50%的条目。

尽量将索引建立在那些区分度高的字段上,或者将区分度较高的字段,放在组合索引的前面。

 

关于Explain

Explain是monogdb的重要性能分析工具。

> db.users.find({"age" : 42}).explain()

{

"cursor" : "BtreeCursor age_1_username_1",

"isMultiKey" : false,

"n" : 8332,

"nscannedObjects" : 8332,

"nscanned" : 8332,

"nscannedObjectsAllPlans" : 8332,

"nscannedAllPlans" : 8332,

"scanAndOrder" : false,

"indexOnly" : false,

"nYields" : 0,

"nChunkSkips" : 0,

"millis" : 91,

"indexBounds" : {

"age" : [

[

42,

42

]

],

"username" : [

[

{         

"$minElement" : 1

},

{

"$maxElement" : 1

}

]

]

},

"server" : "ubuntu:27017"

}

cursor:cursor会显示使用哪个索引,如果为BasicCursor,表示未使用索引,大多数使用索引的查询会使用BtreeCursor。

millis: 如果MongoDB尝试了多种执行方案(mongoDB有Plan Race的策略决定使用哪种方案), "millis" 体现的是花费的总时间,而不是最佳方案的执行时间。

Index entries are described by  "nscanned" . The number of documents scanned is reflected in  nscannedObjects:扫描的文档数

"nscanned" : 如果使用索引,指查看过的索引数。如果是对表扫描,就是指检查过的文档数。

IndexOnly:是否索引覆盖  

"scanAndOrder" : 是存在内存中对结果作排序。(应避免出现)

If MongoDB had to sort results in memor

 

The Query Optimizer

Plan race.:第一个返回100个结果的方案成为“winner”,其它方案停止执行。

 

When not to index

当你要返回的集合占总数的比例越来越大时,索引的作用也就越来越小。因为这会涉及两次查询,一次查index,一次回表。

例如,db.entries.find({"created_at" : {"$lt" : hourAgo}}),当数据量增大,可能返回结果会很多。如果你希望直接作表扫描以表的自然顺序返回结果的话,可以使用hint {"$natural" : 1}。

 

db.currentOp()可以查看当前操作 。

 

唯一索引

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

具有唯一索引的键,就会有唯一约束。“_id”的唯一索引会自动建立无须指定。

   有些情况下,有些量可能无法被索引,而monogdb不返回任何警告。所有的字段都必须小于1024个字节(其中包括字段名和值和命名空间),才能含到索引里。All fields must be smaller than 1024 bytes to be included in an index. 所以,超过1024bytes(书说8KB,应该是指8Kb吧?)大小的键不会受到唯一索引的约束,可以插入多个同样的8KB长的字串,因这些不会被索引。

 

 

组合唯一索引,对usernmae 和age建立组合索引。两个字段都完全相同,才会违返唯一约束。以下insert为legal.

db.users.insert({"username" : "bob"})

> db.users.insert({"username" : "bob", "age" : 23})

> db.users.insert({"username" : "fred", "age" : 23})

 

稀疏索引sparse indexes

与关系型数据库的稀疏索引概念不同,稀疏索引就是索引只包含被索引字段的文档。任何一个稀疏的缺失某一个字段的文档将不会存储在索引中,之所以称之为稀疏索引就是说缺失字段的文档的值会丢失。(不在时,不被存入索引,那么如果存在,但为null,会存入索引吗?还需实验)

如果索引字段可能不存在,又要保证unique,就可以如下方式建立。

db.ensureIndex({"email" : 1}, {"unique" : true, "sparse" : true})

使用sparse indexes 可能会影响查询结果。

不使用索引时

> db.foo.find({"x" : {"$ne" : 2}})

{ "_id" : 0 }

{ "_id" : 1, "x" : 1 }

{ "_id" : 3, "x" : 3 }

但是你如果对“x”建立稀疏索引,

> db.foo.find({"x" : {"$ne" : 2}})

{ "_id" : 1, "x" : 1 }

{ "_id" : 3, "x" : 3 }

 

索引管理

所有关于数据库索引的信息都存在system.indexes集合里面。也可以运行db.collectionName.getIndexes() 来查看一个指定集合的索引信息。

索引名称默认为 keyname1_dir1_keyname2_dir2_..._keynameN_dirN ,keynameX为索引的皱键名,  dirX 指索引的升降序方向 (1 or -1)。

也可以用如下命令指定index名称

> db.foo.ensureIndex({"a" : 1, "b" : 1, "c" : 1, ..., "z" : 1},

... {"name" : "alphabet"})

默认情况下,mongodb会尽快建立索引,阻塞所有读写操作,直到索引被建立完毕。你如果想保证对读写仍有响应,可以使用后台运行的参数来建索引。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics