MongoDB 索引(Index)

一、索引的CRUD

创建索引:ensureIndex()

    db.collection.ensureIndex({"name":1})    为collection的name属性创建正序索引

    db.collection.ensureIndex({"name":1},{unique:true})    为collection的name属性创建正序唯一索引。

     db.collection.ensureIndex({"name":1},{unique:true,dropDups:true})   为collection的name属性创建正序唯一索引。剔除为唯一重复值(在建唯一索引前已经存在重复值)。

 

查看索引:getIndexes()

   db.system.indexes.find()  查看当前数据库的所有索引。 所有数据库都存在索引文档system.indexes

   db.system.namespaces.find() 查看当前数据库的所有索引。

   db.collection.getIndexes()  查询当前数据库中指定collection的索引。

删除索引:dropIndex()

   db.collection.dropIndex({"name":1})    删除collection中的name属性的索引。

   db.collection.dropIndex("name_1")      删除collection中索引名称为name_1的索引

   db.runCommand({dropIndexes:"collection",index:"indexName"})   删除当前数据库中collection文档的索引名称为indexName的索引。注意indexName是索引名称,不是文档的属性名称。

    db.runCommand({dropIndexes:"collection",index:"*"}) 删除当前数据库中collection文档的所有索引。

查询时指定索引:

  Hint 

     eg:db.user.find({name:'张三',sex:'男'}).hint({name:1});   #指定索引key

             db.user.find({name:'张三',sex:'男'}).hint("name_1");     #指定索引名称

     注意:查询时指定索引时指定索引必须存在。

     db.collection.getIndexes();  查询指定集合的索引列表

 

重建索引 reindex()

语法:db.collection.reIndex()

适用场景:一个表经过大量的修改后会导致表的文件上产生空洞,索引文件也是一样,

可以通过重建索引来提高索引的效率。

不好的地方:对于数据量大或者索引多的表来说,重建索引代价重大。

一般不需要重建索引,只有集合大小发生显巨变化或磁盘空间占用不成比例时才需要

二、索引的分类

   1、默认索引

 

    2、单列索引

 

    3、组合索引

 db.collection.ensureIndex({"provinceId":1,"isDied":1});

 

  注意:组合索引支持的索引查询只是索引的任意前缀索引字段。 

eg:

建表:

db.products.insert({

"_id": ObjectId("56c448bb4b3a77a13d33d5d6"),

"item": "Banana",

"category": ["food", "produce", "grocery"],

"location": "4th Street Store",

"stock": 4,

"type": "cases"

});

 新建索引:db.products.ensureIndex({"item":1,"location": 1,"stock": 1})

以下三个查询都不能使用索引:

db.products.find({"location":"4th Street Store"}).explain();

db.products.find({"stock":4}).explain();

db.products.find({"location":"4th Street Store","stock":4}).explain();

下面查询文档上说不能使用索引,但是实际可以使用,不知道为什么 20170220。

db.products.find({"item": "Banana","stock": 4}).explain();

MongoDB 支持索引的前缀查询。

 

    4、内嵌索引

     a,内嵌字段索引(Index on an Embedded Field)

#创建数据
db.records.insert({
  "_id": ObjectId("570c04a4ad233577f97dc459"),
  "score": 1034,
  "location": { state: "NY", city: "New York" }
})
#创建索引
db.records.createIndex( { "location.state": 1 } )

   

#使用索引查询
    #使用了索引    查询到了数据
    db.records.find( { "location.state": "NY" } ).explain();  
    #使用了索引   查询到了数据
    db.records.find( {"location.city": "New York","location.state": "NY" } ).explain();  
    #使用了索引   查询不到数据
    db.records.find( { location: { city: "New York", state: "NY" } } );

   b,内嵌文档索引(Index on an Embedded Documents)

   

#创建数据
db.user.insert(
    {
      name: {
              first: "Yukihiro",
              last: "Matsumoto"
            }
    }
);

db.user.insert(  
    {  
      name: {  
              first: "Yukihiro",  
              last: "Matsumoto",
              center:"center"
            }  
    }  
)
#创建索引
db.user.createIndex( { "name": 1 } );
#使用索引查询
#使用了索引    查询到了数据(第一条数据)
db.user.find( { name: { first: "Yukihiro", last: "Matsumoto" } } ).explain(); 
# 内嵌文档索引必须内嵌文档里面的内容完全匹配才能查出,所以这就是上面的查询为什么只返回了一条数据的理由  

#没有使用索引    查询到了数据(两条数据都查到了)
db.user.find(
   {
     "name.first": "Yukihiro",
     "name.last": "Matsumoto"
   }
).explain();

     内嵌文档索引

#建立索引前查询
db.collection.find({"contents.content":"传统美术"}).explain()
#建立索引
db.collection.ensureIndex({ "contents.content": 1} )
#建立索引后查询
db.collection.find({"contents.content":"传统美术"}).explain()

#建立索引前查询
db.collection.find({"contents":{$elemMatch:{"fieldId":"project_type","content":"传统美术"}}}).explain()
#建立索引
db.collection.ensureIndex({ "contents.fieldId": 1,"contents.content": 1} )
#建立索引后查询
db.collection.find({"contents":{$elemMatch:{"fieldId":"project_type","content":"传统美术"}}}).explain()
#建立索引后性能有了明显的提升。

 

 

    5、唯一索引  {unique:true}

        db.collection.ensureIndex({"name":1},{unique:true})    为collection的name属性创建正序唯一索引

    6、全文索引

        当前数据库开启全文索引:db.adminCommand({setParameter:true,textSearchEnabled:true})

        新建全文索引:db.collection.ensureIndex({"contents.content":"text"});

        使用全文索引进行查询:db.collection.find({$text:{$search:"景区"}})

        参考文章:http://www.ziqiangxuetang.com/mongodb/mongodb-text-search.html

        注意:一张表只能新建一个全文索引。

   7、哈希索引 {'field':'hashed'}

        哈希索引特点:速度比普通索引块,但是无法对范围查询进行优化。哈希索引不适合范围查询,哈希索引只支持单字段的索引,不支持多字段索引。

         新建哈希索引:db.collection.ensureIndex({"createTime":"hashed"});

   8、后台索引

        db.people.ensureIndex({zipcode:1},{background:true})

         后台索引不会像其它索引一样,不是个阻塞操作。

   9、稀疏索引   { sparse: true }

    稀疏索引的特点:如果针对field做索引,针对不含field列的文档将不建立索引。

 

#准备数据
db.user2.insert({'name':'zhangsan'});
db.user2.insert({'name':'lisi'});
db.user2.insert({});

db.user2.find({'name':null});

#唯一索引测试
db.user2.ensureIndex({"name":1},{unique: true})
db.user2.find({'name':null}).explain();  #使用了索引
db.user2.find({'name':'zhangsan'}).explain(); #使用了索引
db.user2.dropIndexes();


db.user2.ensureIndex({"name":1},{sparse: true})
db.user2.find({'name':null}).explain();  #没有使用索引
db.user2.find({'name':'zhangsan'}).explain(); #使用了索引

    注意:db.user2.find({'name':null}).explain();  #没有使用索引,这是稀疏索引和其它索引的区别

 

   10、地理位置坐标索引

       xxxxxx

 

 

 参考文章:http://www.jb51.net/article/39671.htm

                   http://www.cnblogs.com/Amaranthus/p/3574058.html#_Toc381385679

三、建立索引的原则

 

   1、创建的索引要匹配查询

如果你仅仅要查询单个字段。索引这个字段即可。如
db.user.find({"account":"zhangsan"})
这个例子中,唯一索引是最佳的
db.user.ensureIndex({"account":1 }, {unique: true});

   

然而,一般都查询多个键并且排序结果。这种情况,组合索引是最佳的,例子如下
db.project_material.find({ provinceId : '66ef8bd44b3a4a8d6a819120'}).sort({ createTime : -1 });
创建的索引如下
db.project_material.ensureIndex({provinceId : 1, createTime : -1});

 

2、索引列颗粒越小越好

 

3、要小心单键索引的低选择性。

 

4、要关注应用读/写( read/write) 比率,不要随意添加索引

这个很重要,因为,添加索引意味着添加,更新,删除操作变慢。

如果你的应用是偏向于读取,使用索引是非常好的事情。

 

5、先后顺序不一样,效果也不一样

db.item_file.ensureIndex({"displayOrder":1,"itemTitle":1,"itemCode":1});不等于下面的

db.item_file.ensureIndex({"itemTitle":1,"itemCode":1,"displayOrder":1});

 

参考文章:http://www.2cto.com/os/201501/369209.html

 

索引如何设计(读取场景相应的索引设计)

1,查询返回某些字段,而这些字段都可以放在索引中时

  索引覆盖查询,

  这样做的好处是MongoDB就没有必要加载整个文档,通过索引就可以搞定。

2,联合查询

   支持前缀索引查询。

MongoDB有个特性:不是前缀查询无法利用索引。

3,查询返回数据占文档集合数据的比重很大时

   不建立索引查询会更快。

   比如文档200G  索引50G

   使用索引就必须加载到内存250G,如果不使用索引全表扫描 加载到内存只有200G 反而更快。

4,不要到处使用索引,索引的使用要适中。

返回数据量小的文档,建议使用索引。

返回>=50%文档数据时,不建议使用索引

 

四、性能比较

已建立索引和未建索引前后插入效率对比:

var start =new Date();
for(var i=0;i<100000;i++){
	db.log_login_copy1.insert({"user":"ceshi"+i,"fromIP":"113.91.248.225","memo" : "成功登录","logLevel" : "INFO","logModel" : "浏览器"+i});
}
var end = new Date();
print("建了索引插入10万行数据耗时="+(end-start));

 

 

 

var start =new Date();
for(var i=0;i<100000;i++){
	db.log_login_copy2.insert({"user":"ceshi"+i,"fromIP":"113.91.248.225","memo" : "成功登录","logLevel" : "INFO","logModel" : "浏览器"+i});
}
var end = new Date();
print("没建索引插入10万行数据耗时="+(end-start));

 

 

总结:

插入的速度没有网上说的那么快只要三秒左右,为什么呢?

已经建立索引和未建立索引的查询效率差异不大 实际是这样吗?

 

已建立索引和未建索引前后查询效率对比:

查询语句

db.log_login.find({"user":"qinghai"}).explain()

 

建立索引的语句

db.log_login.ensureIndex({"user":1})

  

 没有建立索引查询耗时(总共100万行记录):

 

建立索引后查询耗时(总共100万行记录):

 

 

explain用法: 查看查询计划

  explain()是非常有用的工具,会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以得到查询细节

  eg:db.role.find().explain()

 字段说明:
    cursor:返回游标类型,有BasicCursor和BtreeCursor,后者意味着使用了索引。
    nscanned:扫描document的行数。
    n:返回的文档行数。
    millis:耗时(毫秒)。这个对于比较索引和非索引性能非常有用。
indexBounds:所用的索引。

 


 总结:从上面两幅截图对比可以看出创建索引后速度提升了上百倍

 

问题:

模糊查询为什么使用了索引效率还是非常低下呢?201604  查询用了指定新建的索引后效率至少降低了一半,有什么解决方法吗?   是优化模糊查询的方式解决,还是建立其它索引的方式解决,还是没有解决方法呢?

 

常见错误:

1,Btree::insert: key too large to index 内容太多不能新建索引。

解决方法:在当前数据库下执行下面命令:db.adminCommand({setParameter:true,failIndexKeyTooLong:false});

 

 2,如果执行查询报下面错误

com.mongodb.MongoException: Runner error: Overflow sort stage buffered data usage of 33555102 bytes exceeds internal limit of 33554432 bytes

那提取出查询语句:

db.item_file.find({ "itemTitle" : { "$regex" : "^.*?.*$" , "$options" : "i"} , "itemCode" : { "$regex" : "^.*?.*$" , "$options" : "i"}}).sort({ "displayOrder" : 1}).skip(58860).limit(10);

新建下面索引:

db.item_file.ensureIndex({"displayOrder":1});

但是为什么不是新建下面的索引呢? 20160321.  后面有时间的时候查查。

db.item_file.ensureIndex({"itemTitle":1,"itemCode":1,"displayOrder":1});

新建下面这个索引是可以的。

db.item_file.ensureIndex({"displayOrder":1,"itemTitle":1,"itemCode":1});

 

 

 

  

猜你喜欢

转载自wangshirufeng.iteye.com/blog/2264633