练习 MongoDB 操做 —— 索引篇(二)

本文围绕索引、游标两部分进行探索,对MongoDB数据库的索引部分有一个大概的了解;html

索引

索引一般可以极大的提升查询的效率,若是没有索引,MongoDB在读取数据时必须扫描集合中的每一个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是很是低的,特别在处理大量的数据时,查询能够要花费几十秒甚至几分钟,这对网站的性能是很是致命的。正则表达式

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。索引,从建立的形式上能够分为普通索引复合索引数组索引子文档索引mongodb

应该了解的索引限制,shell

  • 开销:每一个索引占据必定的存储空间,在进行插入,更新和删除操做时也须要对索引进行操做。因此,若是你不多对集合进行读取操做,建议不使用索引;
  • 内存(RAM)使用:因为索引是存储在内存中,你应该确保该索引的大小不超过内存的限制。若是索引的大小大于内存的限制,MongoDB会删除一些索引,这将致使性能降低。
  • 最大范围:数据库

    (1)集合中索引不能超过 64 个;
    (2)索引名的长度不能超过 125 个字符;
    (3)一个复合索引最多能够有 31 个字段
  • 索引不能被如下的查询使用;能够调用explain()方法查看是否使用索引;数组

    (1)正则表达式及非操做符,如 `$nin`, `$not`, 等
    (2)算术运算符,如 `$mod` 等
    (3)`$where` 子句

操做

建立索引

为集合grade_1_4根据性别和年龄建立复合索引:数据结构

db.getCollection('grade_1_4').ensureIndex({"sex": 1, "age": 1})

会获得结果:dom

{
    "v" : 2,                  // 索引的版本号。默认的索引版本取决于mongod建立索引时运行的版本
    "key" : {
        "sex" : 1,
        "age" : 1
    },
    "name" : "sex_1_age_1",
    "ns" : "school.grade_1_4"
}

说明:基于sexage的查询将会用到该复合索引,或者是基于sex的查询也会用到该索引,可是只是基于age的查询将不会用到该复合索引。所以能够说,若是想用到复合索引,必须在查询条件中包含复合索引中的前N个索引列。然而若是查询条件中的键值顺序和复合索引中的建立顺序不一致的话,MongoDB能够智能的帮助咱们调整该顺序,以便使复合索引能够为查询所用性能

若是分别为性别和年龄建立索引:学习

db.getCollection('grade_1_4').ensureIndex({"sex": 1})
db.getCollection('grade_1_4').ensureIndex({"age": 1})

会获得这样的结果:

{
    "v" : 2,
    "key" : {
        "sex" : 1
    },
    "name" : "sex_1",
    "ns" : "school.grade_1_4"
},
{
    "v" : 2,
    "key" : {
        "age" : 1
    },
    "name" : "age_1",
    "ns" : "school.grade_1_4"
}

查看索引

查看集合grade_1_4已经建立的索引规则:

db.getCollection('grade_1_4').getIndexes()

查看集合grade_1_4索引占用内存空间的大小,单位是字节:

db.getCollection('grade_1_4').totalIndexSize()

删除索引

删除集合grade_1_4namesex_1的索引规则:

db.getCollection('grade_1_4').dropIndex({"sex": 1})

惟一索引

建立惟一索引与普通索引的区别在于,多一个可选参数 unique;举个例子,根据姓名建立惟一索引:

db.getCollection('grade_1_4').ensureIndex({"name":1}, {"unique":true})

建立惟一索引可以保证每条记录的name字段值是不重复的;

当一个文档以惟一索引的方式保存到集合中去的时候,任何缺失的索引字段都会以null值代替,所以,不能在惟一索引上同时插入两条缺省的记录。

假设集合已经存在一些记录,在些基础上建立惟一索引;此时,对这些已经存在的记录,若是索引项值是存在重复的,则建立索引时会报错;若是必定要在这样的键上建立惟一索引,那么系统将保存第一条记录,剩下的记录会被删除,结合dropDups参数使用(此参数只能在mongodb 3.0版本以前使用):

db.getCollection('grade_1_4').ensureIndex({"name":1}, {unique:true, dropDups: true})

稀疏索引

稀疏索引的建立和彻底索引的建立没有什么不一样。使用稀疏索引进行查询的时候,某些因为缺失了字段的文档记录可能不会被返回,这是因为稀疏索引子返回被索引了的字段。

示例:

> db.people.ensureIndex({title:1},{sparse:true}) //在title字段上创建稀疏索引
> db.people.save({name:"Jim"})
> db.people.save({name:"yang",title:"prince"})
> db.people.find();
{ "_id" : ObjectId("4e244dc5cac1e3490b9033d7"), "name" : "Jim" }
{ "_id" : ObjectId("4e244debcac1e3490b9033d8"), "name" : "yang", "title" : "prince" }

> db.people.find().sort({title:1})//自有包含有索引字段的记录才被返回
{ "_id" : ObjectId("4e244debcac1e3490b9033d8"), "name" : "yang", "title" : "prince" }

> db.people.dropIndex({title:1})//删除稀疏索引以后,全部的记录均显示
{ "nIndexesWas" : 2, "ok" : 1 }

> db.people.find().sort({title:1})
{ "_id" : ObjectId("4e244dc5cac1e3490b9033d7"), "name" : "Jim" }
{ "_id" : ObjectId("4e244debcac1e3490b9033d8"), "name" : "yang", "title" : "prince" }

性能示例

建立三十万条数据

for (var i = 1; i <= 300000; i++) {
    db.getCollection('grade_1_5').insert({
       "name": "zhangsan" + i,
       "sex": Math.round(Math.random() * 10) % 2,
       "age": Math.round(Math.random() * 6) + 3
   });
}

根据姓名查一些特定的人

db.getCollection('grade_1_5').find(
    {
        $or: [{"name":"zhangsan10000"},{"name":"zhangsan14000"},{"name":"zhangsan9000"},{"name":"zhangsan23000"},{"name":"zhangsan24050"},
              {"name":"zhangsan12000"},{"name":"zhangsan14300"},{"name":"zhangsan9300"},{"name":"zhangsan23300"},{"name":"zhangsan24350"},
              {"name":"zhangsan11100"},{"name":"zhangsan15200"},{"name":"zhangsan8100"},{"name":"zhangsan22100"},{"name":"zhangsan26150"},
              {"name":"zhangsan10200"},{"name":"zhangsan14020"},{"name":"zhangsan9020"},{"name":"zhangsan23020"},{"name":"zhangsan24070"},
              {"name":"zhangsan10300"},{"name":"zhangsan14030"},{"name":"zhangsan9030"},{"name":"zhangsan23030"},{"name":"zhangsan24080"}]
    }
)

对姓名创建索引,会占用5873664字节

db.getCollection('grade_1_5').ensureIndex({"name": 1})

对于上面的命令,经过调整顺序,观察时间,对性能有一些大概的了解;

行为 建立-查询-建索引 建立-建索引-查询 建索引-建立-查询
建立时间 150.957s 150.957s 159.967s
查询时间 1.024s 0.005s 0.005s
创建索引时间 0.527s 0.527s 0.009s

游标

直接对一个集合调用find()方法时,咱们会发现,若是查询结果超过二十条,只会返回二十条的结果,这是由于Mongodb会自动递归find()返回的是游标。

var cursor = db.getCollection('grade_1_4').find({});

执行上述命令时,shell并不会真正地访问数据库,而是等待开始要求得到结果的时候才向数据库发送查询请求。

此时能够对这个游标进行各类设置,而后调用游标的hashNext()next()方法,这样就会真正访问数据库,这是一个懒加载的过程,以下

var cursor = db.getCollection('grade_1_4').find({});

while(cursor.hasNext()){  
    var doc = cursor.next();  
    // do stuff with doc  
};

能够基于游标,进行limit、skip、sort操做,通常应用于分页场景;

  • limit:限制游标返回的数量,指定了上限;
  • skip :忽略前面的部分文档,若是文档总数量小于忽略的数量,则返回空集合;
  • sort :对获得的子集合进行排序,能够按照多个键进行正反排序;

上述三个命令能够进行链式操做;

附录

如下是文章会用到的参考,及有意义的扩展阅读;

相关文章
相关标签/搜索