索引是用来加快查询速度的,事物都有双面性的,同时在每次插入、更新和删除操作时都会产生额外的开销。索引有时并不能解决查询慢的问题,一般来说,返回集合中一半以上的结果,全表扫描要比查询索引更高效些。创建太多索引,会导致插入非常慢,同时还会占用很大空间。可以通过explain和hint工具来分析。mongo 的索引非常强大,和关系型数据库索引没什么区别。MongoDB中索引是大小写敏感的。
查看索引
db.demo.getIndexes();
删除集合所有索引:
> db.collection.dropIndexes()
删除集合某个索引:
> db.collection.dropIndex({x:1})
mongodb使用createIndex方法来创建索引。语法:
db.COLLECTION_NAME.createIndex(keys[,options])
当有大量数据时,创建索引会非常耗时,可以指定到后台执行,只需指定“backgroud:true”即可。
> db.ttlsa_posts.ensureIndex({pid:1},{backgroud:true});
嵌入式索引 为内嵌文档的键创建索引与普通的键创建索引并无差异。
> db.ttlsa_posts.ensureIndex({"post.date":1})
创建唯一索引
我们也可以定义唯一索引,方法就是指定unique键为true,如:
>> db.users.createIndex({email:1},{'unique':true})
当为已有的集合创建索引,可能有些数据已经有重复了的,那么创建唯一索引将失败。可以使用dropDups来保留第一个文档,而后的重复文档将删除,这种方法慎重操作。
> db.ttlsa_posts.ensureIndex({pid:1},{unique:true, dropDups:true})
强制索引 hint命令可以强制使用某个索引
> db.ttlsa_posts.find({pid:{$lt:333}}).hint({pid:1,date:1})
创建部分索引语法:
db.collection.createIndex(keys, options)
options可以使用partialFilterExpression,即部分过滤表达式,其类型为文档类型
过滤表达式通常包括:$exists, $gt, $gte, $lt, $lte,$type,$and
过滤表达式使用示例:
db.persons.createIndex({name:1},{partialFilterExpression:{age: {$gt:25}}})
此句的意思是:基于age列创建大于25岁的部分索引。创建的部分索引过滤条件是age大于25,当查询的条件是country等于china,age大于25。 条件满足,从执行计划里可以看出此次查询采用索引扫描。
文本索引
MongoDB提供文本索引以支持对字符串内容的文本搜索查询。text索引可以包括其值为字符串或字符串元素数组的任何字段。
文本索引,顾名思义就是用于搜索文本的,可以用于搜索所有的value,也可以搜索指定的field对应的value。只要field对应value是string,或者对应的value是array且array中的元素是string,那么文本索引都可以索引该field.注意:一个集合最多只能有一个文本索引。如以下示例所示:
db.collection.createIndex({keys:”text”})
也可以创建多个字段text,例如
db.collection.createIndex({subject:”text”,comments:”text”})
查询
db.collection.find({$text:{$search:”MongoDB best”}})
查询出来的结果集是:
{_id:5908df789dfd1fd5884fd84f7df4,statement:MongoDB is the worst}
{_id:5908dfgfh587hgf15f4hf54hf418,statement:MongoDB is the best}
这是因为文本查询时,每个单词之间的分隔是”或者”,所以上面的查询语句的意思是:查询包含MongoDB或者best的记录数。
通过explain结果来分析性能
我们往往会通过打点数据来分析业务的性能瓶颈,这时,我们会发现很多瓶颈都是出现在数据库相关的操作上,这时由于数据库的查询和存取都涉及大量的IO操作,而且有时由于使用不当,会导致IO操作的大幅度增长,从而导致了产生性能问题。而MongoDB提供了一个explain工具来用于分析数据库的操作。直接拿官网的示例来做说明:
假设我们在inventory collection中有如下文档:
{ "_id" : 1, "item" : "f1", type: "food", quantity: 500 }
{ "_id" : 2, "item" : "f2", type: "food", quantity: 100 }
{ "_id" : 3, "item" : "p1", type: "paper", quantity: 200 }
{ "_id" : 4, "item" : "p2", type: "paper", quantity: 150 }
{ "_id" : 5, "item" : "f3", type: "food", quantity: 300 }
{ "_id" : 6, "item" : "t1", type: "toys", quantity: 500 }
{ "_id" : 7, "item" : "a1", type: "apparel", quantity: 250 }
{ "_id" : 8, "item" : "a2", type: "apparel", quantity: 400 }
{ "_id" : 9, "item" : "t2", type: "toys", quantity: 50 }
{ "_id" : 10, "item" : "f4", type: "food", quantity: 75 }
假设此时没有建立索引,做如下查询:
db.inventory.find( { quantity: { $gte: 100, $lte: 200 } } )
返回结果如下:
{ "_id" : 2, "item" : "f2", "type" : "food", "quantity" : 100 }
{ "_id" : 3, "item" : "p1", "type" : "paper", "quantity" : 200 }
{ "_id" : 4, "item" : "p2", "type" : "paper", "quantity" : 150 }
这是我们可以通过explain来分析整个查询的过程:
# explain 有三种模式: "queryPlanner", "executionStats", and "allPlansExecution".
# 其中最常用的就是第二种"executionStats",它会返回具体执行的时候的统计数据
db.inventory.find(
{ quantity: { $gte: 100, $lte: 200 } }
).explain("executionStats");
查询结果中有一个"stage"字段,而MongoDB总共有如下几种stage:
- COLLSCAN – 全表扫描
- IXSCAN – 索引扫描
- FETCH – Retrieving documents
- SHARD_MERGE – Merging results from shards
- SORT – Explicit sort rather than using index order
碰到索引不一致的情况,想从A库中导出索引在B库执行
var collectionList = db.getCollectionNames();
for(var index in collectionList){
var collection = collectionList[index];
var cur = db.getCollection(collection).getIndexes();
if(cur.length == 1){
continue;
}
for(var index1 in cur){
var next = cur[index1];
if(next["_id"] == '1'){
continue;
}
print("try{ db.getCollection(\""+collection+"\").ensureIndex("+JSON.stringify(next)+",{background:1})}catch(e){print(e)}");
}
}