链接地址: 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会尽快建立索引,阻塞所有读写操作,直到索引被建立完毕。你如果想保证对读写仍有响应,可以使用后台运行的参数来建索引。