MongoDB入门学习(四):MongoDB的索引

        上一篇讲到了MongoDB的基本操做增删查改。对于查询来讲。必须依照咱们的查询要求去集合中,并将查找到的结果返回。在这个过程当中事实上是对整个集合中每个文档进行了扫描,假设知足咱们的要求就加入到结果集中最后返回。对于小集合来讲。这个过程没什么,但是集合中数据很是大的时候,进行表扫描是一个很是恐怖的事情,因而有了索引一说,索引是用来加速查询的。至关于书籍的文件夹。有了文件夹可以很是精准的定位要查找内容的位置,从而下降无谓的查找。css

1.索引的类型html

        建立索引可以是在单个字段上,也可以是在多个字段上,这个依据本身的实际状况来选择,建立索引时字段的顺序也是有讲究的。数据库

建立索引是经过ensureIndex()方法。需要给该方法传递一个文档形式的数据,当中指定索引的字段和顺序,1表明升序。-1表明降序。数组

        1).默认索引dom

           还记得"_id"吗,这个字段的数据是不能反复的。它就是MongoDB的默认索引。而且不能被删除。工具

        2).单列索引post

           在单个字段上建立的索引就是单列索引,在查询的过程当中可以对该加速对该键的查询,然而对其它键的查询是没有帮助的。spa

单列索引的顺序是不会影响对该键的随即查询。建立单列索引:.net

> db.people.ensureIndex({"name" : 1})

        3).组合索引code

           还可以在多个键上建立组合索引,此时键的位置和索引的顺序都会影响查询的效率。看如下建立组合索引:

> db.people.ensureIndex({"name" : 1, "age" : 1})
> db.people.ensureIndex({"age" : 1, "name" : 1})

           第一种状况会对name排序组织,当name同样时在对age排序,因此对{"name" : 1}和{“name” : 1, "age" : 1}的查询更高效,而另一种状况则对age排序。当age同样再对name排序。因此对{"age" : 1}和{"age" : 1, "name" : 1}的查询更高效。当组合索引包括很是多字段的时候。会对前几个键的查询有帮助。

        4).内嵌文档索引

           还可以对内嵌文档建立索引,和普通键建立索引同样几乎相同。也可以对内嵌文档建立组合索引:

> db.people.ensureIndex({"friends.name" : 1})
> db.people.ensureIndex({"friends.name" : 1, "friends.age" : 1})

        在来看看其它几种形式的索引:

惟一索引
> db.people.ensureIndex({"name" : 1}, {"unique" : true})
> db.people.ensureIndex({"name" : 1}, {"unique" : true, "dropDups" : true})
松散索引
> db.people.ensureIndex({"name" : 1}, {"sparse" : true})
多值索引
> db.people.find()
{"name" : ["mary", "rose"]}
> db.people.ensureIndex({"name" : 1})

        惟一索引unique可以保证该键相应的值在集合中是惟一的,假设建立惟一索引的时候,该字段原来就存在了反复的数据,那么就会建立失败,可以加上dropDups字段来消除反复数据。它会保留发现的第一个文档。其它有反复数据的文档都将被删除。

        集合中有的文档不存在某些字段。或者某些字段的值为null,那么咱们在该字段上建立索引的时候不但愿让这些空值的文档參与。那么就定义为松散索引sparse。比方在name上建立索引时,发现有的人在数据库中仅仅有学号。没有名字。那么咱们不但愿把它们也包括进来。此时就定义为松散索引。

        一个键相应的值是一个数组,在该键上建立索引时是一个多值索引。会为数组中每个值生成一个索引元素。至关于分裂成了几个独立的索引项,但是它们仍是相应同一个文档数据。

2.索引的管理

        索引当然是为查询而生,而且可以为每个键都建立索引。但是索引是需要存储空间的,因此索引不是越多越好,而且建立索引后。每次的插入,更新和删除文档都会产生额外的开销,因为数据库中不但要运行这些操做,而且还要在集合索引中标记这些操做。因此要依据实际状况来建立索引,索引没用以后将其删除。

        建立索引是ensureIndex()方法,建立完毕后可以经过getIndexes()来查看集合中建立的索引状况:

> db.people.ensureIndex({"name" : 1, "age" : 1})
> db.people.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "test.people",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "name" : 1,
                        "age" : 1
                },
                "ns" : "test.people",
                "name" : "name_1_age_1"
        }
]

        可以看到people集合建立了两个索引,一个是"_id",这个是默认索引。另一个是name和age的组合索引,名字为keyname1_dir_keyname2_dir_...,keyname表明索引的键,dir表明方向。1表明升序,-1表明降序。

固然咱们也可以本身定义索引的名称:

> db.people.ensureIndex({"name" : 1, "age" : 1}, {"name" : "myIndex"})
> db.people.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "test.people",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "name" : 1,
                        "age" : 1
                },
                "ns" : "test.people",
                "name" : "myIndex"
        }
]

        删除索引是经过dropIndex():

方式一:
> db.people.dropIndex({"name" : 1, "age" : 1})
{ "nIndexesWas" : 2, "ok" : 1 }
方式二:
> db.runCommand({"dropIndexes" : "people", "index" : "myIndex"})
{ "nIndexesWas" : 2, "ok" : 1 }

        索引的元信息存储在每个数据库的system.indexes集合中。不能对其进行插入和删除文档的操做。仅仅能经过ensureIndex和dropIndex进行。

> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "test.people", "name" : "_id_" }
{ "v" : 1, "key" : { "name" : 1, "age" : 1 }, "ns" : "test.people", "name" : "myIndex" }

        清空集合中全部的文档是不会将索引删除的,原来建立的索引依旧存在,但是直接删除集合的话,该集合的索引也是会被删除的。

3.索引的效率

        假设咱们定义了很是多的索引,那么MongoDB会依据咱们的查询选项又一次排序,并智能的选择一个最优的来使用。比方咱们建立了{"name" : 1, "age" : 1}和{"age" : 1, "class" : 1}两个索引,但是咱们的查询项为find({"age" : 10, "name" : "mary"}),那么MongoDB会本身主动又一次排序为find({"name" : "mary", "age" : 10}),而且利用索引{"name" : 1, "age" : 1}来查询。

        MongoDB提供了explain工具来帮助咱们得到查询方面的很是多实用信息,仅仅要对游标调用这种方法就可以获得查询的细节。如下给math集合中加入10W个文档。再来看看使用索引先后的效率对照:

> var arr = [];
> for(var i = 0; i < 100000; i++){
... var doc = {};
... var value = Math.floor(Math.random() * 1000);
... doc["number"] = value;
... arr.push(doc);
... }
100000
> db.math.insert(arr)
> db.math.count()
100000
> db.math.find().limit(10)
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fe5"), "number" : 462 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fe6"), "number" : 123 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fe7"), "number" : 90 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fe8"), "number" : 46 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fe9"), "number" : 244 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fea"), "number" : 972 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61feb"), "number" : 925 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fec"), "number" : 110 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fed"), "number" : 739 }
{ "_id" : ObjectId("53a7f7c6e4fd24348ce61fee"), "number" : 945 }

        经过for循环给arr数组中加入10W条数据。而后再批量插入这些数据到math集合中。查看前10条数据,因为是随即生成的值。因此number字段的值会有反复值。咱们就来查询462这个值:

建立索引前:
> db.math.find({"number" : 462}).explain()
{
        "cursor" : "BasicCursor",
        "isMultiKey" : false,
        "n" : 94,
        "nscannedObjects" : 100000,
        "nscanned" : 100000,
        "nscannedObjectsAllPlans" : 100000,
        "nscannedAllPlans" : 100000,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 35,
        "indexBounds" : {

        },
        "server" : "server0.169:9352"
}
建立索引后:
> db.math.ensureIndex({"number" : 1})
> db.math.find({"number" : 462}).explain()
{
        "cursor" : "BtreeCursor number_1",
        "isMultiKey" : false,
        "n" : 94,
        "nscannedObjects" : 94,
        "nscanned" : 94,
        "nscannedObjectsAllPlans" : 94,
        "nscannedAllPlans" : 94,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "number" : [
                        [
                                462,
                                462
                        ]
                ]
        },
        "server" : "server0.169:9352"
}

        这里来看一下实用的信息,"cursor"指用的哪一个索引。"nscanned"表明查找了多少个文档。"n"指返回文档的数量。"millis"表示查询所花时间。单位是毫秒。可以看出建立索引前没有使用索引。在全部的文档中查询的,花费了35毫秒。而建立索引后,使用了number_1索引查询。索引存储在B树结构中,仅仅在94个文档中查询,差点儿不花时间。

        假设有很是多索引的话,MongoDB会本身主动选一个来查询,你也可以经过hint来强制使用某个索引,这里强制使用{"age" : 1, "name" : 1}这个索引:

> db.people.find({"age" : {"$gt" : 10}, "name" : "mary"}).hint({"age" : 1, "name" : 1})
相关文章
相关标签/搜索