MongoDB中常见的索引有单字段索引和复合索引.
>db.users.find({username:"user0115"}).explain(true)
//以下是其中的一部分, 可以看到docsExamined为100W,即扫描了所有的文档, 查询时间为executionTimeMillis为318ms
//db.users.find({username:"user123456"}).explain(true).executionStats.executionTimeMillis 可以直接查看花费的时间
"executionStats" : {
.....
"executionSuccess" : true,
"executionTimeMillis" : 318,
"nReturned" : 1,
"totalDocsExamined" : 1000000,
"totalKeysExamined" : 0
.....
}
这次我们对username新建一个索引, 1表示增序排列:
>db.users.ensureIndex({"username":1})
db.users.find({username:"user0115"}).explain(true)
//返回如下, 其中indexName表示使用到了这个索引, executionTimeMillis为0ms,totalDocsExamined为1, 只扫描了1个文档, 改变非常的大
....
"indexName" : "username_1",
"indexVersion" : 1,
"invalidates" : 0,
"isEOF" : 1,
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"username" : 1
}
......
"executionSuccess" : true,
"executionTimeMillis" : 0,
"nReturned" : 1,
"totalDocsExamined" : 1,
"totalKeysExamined" : 1
.....
新建索引后, 每次写操作(插入, 更新, 删除)文档都要更新索引. MongoDB限制每个集合上最多只能有64个索引. 通常在一个特定的集合上, 不应该拥有两个以上的索引.
通常索引应该建立在常用的查询的字段上, 对于不常用的字段不应该建立索引.
索引所对应的值是按一定顺序排列的,因此使用索引对文档进行排序非常快.然而进行排序时, 只能使用到一个索引, 我们需要把希望使用的索引放到第一个位置.
在以下的例子中, 由于集合中数据量比较大, 直接排序会超过MongoDB对单次操作的内存限制32M. 所以我们在以下的操作使用的users集合数据量为10W.
>db.users10W.find().sort({"age": 1 , "username" : 1}).limit(1000)
上面的例子中先对age进行排序, 然后再对username排序.我们已经对username建立了索引, 但是这个查询并不能使用到username索引.
查询其explain信息
.....
"memLimit" : 33554432,
"memUsage" : 98341,
.....
"executionSuccess" : true,
"executionTimeMillis" : 124,
"nReturned" : 1000,
"totalDocsExamined" : 100000,
"totalKeysExamined" : 0
.....
发现其内存使用了98K, 扫描了所有的10W条文档, 耗时124ms.
如果这个查询比较普遍的话, 可以在对这两个字段一起建立索引, 称为复合索引(compound index). 如果查询中有多个排序或者查询条件中有多个键, 这个索引就会很有用.
>db.users10W.ensureIndex({"age":1,"username":1})
>db.users10W.getIndexes()
{
"key" : {
"_id" : NumberInt("1")
},
"name" : "_id_",
"ns" : "test.users10W",
"v" : NumberInt("1")
},
{
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "test.users10W",
"v" : NumberInt("1")
},
{
"key" : {
"age" : 1,
"username" : 1
},
"name" : "age_1_username_1",
"ns" : "test.users10W",
"v" : NumberInt("1")
}
可以看到第3个是刚刚新加的复合索引.
再次进行上面的查询和explain信息. 返回1000个文档, 也只扫描了1000个文档, 耗时1ms. 之前的索引没有使用时, 扫描了所有文档, 耗时124ms.
...
"executionSuccess" : true,
"executionTimeMillis" : 1,
"nReturned" : 1000,
"totalDocsExamined" : 1000,
"totalKeysExamined" : 1000
.....
"indexName" : "age_1_username_1",
"indexVersion" : 1,
"isMultiKey" : false,
"isPartial" : false,
"isSparse" : false,
"isUnique" : false,
"keyPattern" : {
"age" : 1,
"username" : 1
},
....
不管是简单索引还是复合索引. 在使用正确的情况下, 都大大加快了查询速度.
语句 | 集合大小 | 索引情况 | 花费时间 | 扫描文档数 |
---|---|---|---|---|
db.users.find({username:”user0115”}) | 1M | 无 | 318ms | 1M |
db.users.find({username:”user0115”}) | 1M | db.users.ensureIndex({“username”:1}) | 0ms | 1 |
db.users10W.find().sort({“age”: 1 , “username” : 1}).limit(1000) | 0.1M | 无 | 124ms | 0.1M |
db.users10W.find().sort({“age”: 1 , “username” : 1}).limit(1000) | 0.1M | db.users10W.ensureIndex({“age”:1,”username”:1}) | 1ms | 1000 |
要点:
查询索引:
db.users10W.getIndexes()
建立索引:
简单索引: db.users.ensureIndex({“username”: 1})
复合索引: db.users10W.ensureIndex({“age”: 1,”username”: 1})
删除索引
db.users10W.dropIndex(“username_1”)