MongoDB索引原理和具体使用

1. MongoDB 索引是用来干嘛?

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

简单举例分析下:

假设这里有一个 commits 集合,我们想要查询其中的数据!

db.commits.find({"eId":"5913cd0ee727e70007a109f2"}).explain("executionStats")

在这里插入图片描述
从执行计划来看,我们从18284条数据中查询得到了80条查询结果集,而我们文档中的记录数就是18284条记录,说明这条查询语句是全表搜索,显然这是非常浪费效率的。

2. 创建单索引

为了提高查询效率,我们可以使用创建索引的方式,mongodb使用createIndex方法来创建索引语法:

db.collection_name.createIndex(keys[,options])

这时我们给commits集合的eId这个字段创建索引

 db.commits.createIndex({eId:1})

索引创建成功之后,这时再查询commits集合

db.commits.find({"eId":"5913cd0ee727e70007a109f2"}).explain("executionStats")

在这里插入图片描述

我们查询得到的数据是80条,而我们一共只在80条数据里查询,从执行记划来看,查询的记录数,查询的时间明显都缩小。
在这里插入图片描述

从上图,如果我们想给sinfo下面的name加索引,可以这样写

db.commits.createIndex({"sinfo.name":1})

我们不在要sinfo上面加索引,如果这样做了,我们必须查询整个子方档。

createIndex()接收可选參数,可选參数列表例如以下:

ParameterTypeDescription
backgroundBoolean建索引过程会堵塞其他数据库操作,background可指定以后台方式创建索引,即添加 “background” 可选參数。 “background” 默认值为false。
uniqueBoolean建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
namestring索引的名称。假设未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDupsBoolean在建立唯一索引时是否删除反复记录,指定 true 创建唯一索引。默认值为 false.
sparseBoolean对文档中不存在的字段数据不启用索引。这个參数须要特别注意。假设设置为true的话,在索引字段中不会查询出不包括相应字段的文档.。默认值为 false.
expireAfterSecondsinteger指定一个以秒为单位的数值,完毕 TTL设定,设定集合的生存时间。
versionindex version索引的版本号号。默认的索引版本号取决于mongod创建索引时执行的版本号。

比如说:创建唯一索引,方法就是指定unique键为true,如:

db.users.createIndex({name:1},{“unique”:true})

3. 复合索引

当我们的查询条件不只有一个时,就需要建立复合索引。复合索引是两个或更多字段的索引,并且它可以支持基于这些字段的查询。当我们查询时有多个过滤条件时,为了提高查询效率,可以在这多个条件上添加索引,语法

db.collection_name.createIndex(key1:1,key2:1)

当我们查询条件为两个时,如:

db.commits.find({"eId":"5913cd0ee727e70007a109f2","sId":"5913c700434a8b00077256fe"}).explain("executionStats")

在这里插入图片描述
因前面已经给eId创建了索引,所以本次查询范围从18248条记录,降到了80条记录,而sId没有创建索引,所有我们从80条记录里得到了符合eId,sId这两个条件的38条记录,那么我们现在给sId也创建索引

db.commits.createIndex({sId:1})

当查询的两个条件sId,eId都创建了索引后,再执行

db.commits.find({"eId":"5913cd0ee727e70007a109f2","sId":"5913c700434a8b00077256fe"}).explain("executionStats")

在这里插入图片描述

4. 多键索引

如果文档中含有array类型字段,可以直接对其名称建立索引,这样MongoDB就会为内嵌数组中的每个元素建立一个独立的索引

注意:多键索引不等于在多列字段上创建索引(复合索引),多键索引与单键索引创建形式相同,差别在于字段的类型.

语法:

db.COLLECTION_NAME.createIndex({key:< 1 or -1 >})

如:

有一个paper集合,数据表结构如下,其中structures字段是数组类型。
在这里插入图片描述
例如:这时我们给papers集合的structures这个字段创建索引

 db.papers.createIndex({structures:1})

因为这个structures字段是数组,所有这个索引称之为多键索引。

5.复合多键索引

对于一个复合多键索引,每个索引最多可以包含一个数组。在多于一个数组的情形下来创建复合多键索引不被支持。

假定存在如下集合

{ _id: 1, a: [ 1, 2 ], b: [ 1, 2 ], category: "AB - both arrays" }

你可能会这样创建索引db.COLLECTION_NAME.createIndex({a:1,b:1}),但是这样是不允许的,因为a和b都是数组。

如果{a:1,b:1}的索引已经创建了,则a和b当中必定有一个是非array,此时插入一个a和b都是array的文档就会失败。

假定存在如下集合

 { _id: 1, a: [1, 2], b: 1, category: "A array" }
{ _id: 2, a: 1, b: [1, 2], category: "B array" }

则可以基于每一个文档创建一个基于{ a: 1, b: 1 }的复合多键索引,原因是每一个索引的索引字段只有一个数组

类似的,如下的内嵌文档也可以建立索引。例如:

db.test.createIndex({“stock.size”:1,”stock.quantity”:1})

在这里插入图片描述

6.部分索引

MongoDB部分索引只为那些在一个集合中,满足指定的筛选条件的文档创建索引。由于部分索引是一个集合文档的一个子集,因此部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本。

部分索引通过指定过滤条件来创建,可以为MongoDB支持的所有索引类型使用部分索引。

简单点说:部分索引就是带有过滤条件的索引,即索引只存在与某些文档之上

创建部分索引语法:

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。 条件满足,从执行计划里可以看出此次查询采用索引扫描。
在这里插入图片描述
当查询的条件是country等于china,age大于等于25。 条件不满足,从执行计划里可以看出此次查询采用的是全表扫描方式。
在这里插入图片描述

创建部分唯一索引的一些限制:

部分索引只为集合中那些满足指定的筛选条件的文档创建索引。 如果你指定的partialfilterexpression和唯一约束、那么唯一性约束只适用于满足筛选条件的文档。 具有唯一约束的部分索引不会阻止不符合唯一约束且不符合过滤条件的文档的插入。

示例文档

db.users.insertMany([
{ "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 },
{ "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 },
{ "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }])

为集合添加索引

db.users.createIndex({ username: 1 },
{ partialFilterExpression: { age: { $gte: 21 } } })

在集合users上插入用户名相同的文档,收到了重复键的错误提示

db.users.insert( { username: "david", age: 27 } )
WriteResult({
        "nInserted" : 0,
        "writeError" : {
                "code" : 11000,
                "errmsg" : "E11000 duplicate key error collection: test.users index: username_1 dup key: { : \"david\" }"}
})

下面插入年龄小于部分索引值或者age键为空的同用户名文档,可以成功插入。
也就是说对于不在部分索引限制之类的其他键值重复是允许的。

db.users.insert( { username: "david", age: 20 } )
WriteResult({ "nInserted" : 1 })

db.users.insert( { username: "amanda" } )
WriteResult({ "nInserted" : 1 })

db.users.insert( { username: "rajiv", age: null } )
WriteResult({ "nInserted" : 1 })

7.文本索引

MongoDB提供文本索引以支持对字符串内容的文本搜索查询。text索引可以包括其值为字符串或字符串元素数组的任何字段。

文本索引,顾名思义就是用于搜索文本的,可以用于搜索所有的value,也可以搜索指定的field对应的value。只要field对应value是string,或者对应的value是array且array中的元素是string,那么文本索引都可以索引该field

注意:一个集合最多只能有一个文本索引。

要创建text索引,请使用该 db.collection.createIndex()方法。要索引包含字符串或字符串元素数组的字段,请包含该字段并"text"在索引文档中指定字符串文字。

如以下示例所示:

db.collection.createIndex({keys:”text”})

也可以创建多个字段text,例如

db.collection.createIndex({subject:”text”,comments:”text”})

举例,有两条记录

{_id:5908df789dfd1fd5884fd84f7df4,statement:MongoDB is the worst}

{_id:5908dfgfh587hgf15f4hf54hf418,statement:MongoDB is the best}

给statement字段创建文本索引:

db.collection.createIndex({statement:”text”})

查询

db.collection.find({$text:{$search:”MongoDB best”}})

查询出来的结果集是:

{_id:5908df789dfd1fd5884fd84f7df4,statement:MongoDB is the worst}

{_id:5908dfgfh587hgf15f4hf54hf418,statement:MongoDB is the best}

这是因为文本查询时,每个单词之间的分隔是”或者”,所以上面的查询语句的意思是:查询包含MongoDB或者best的记录数。

在多个字段上创建文本索引时,还可以使用通配符说明符($**)。使用通配符文本索引,MongoDB会为包含集合中每个文档的字符串数据的每个字段编制索引。

例如:

db.collection.createIndex({“$**,”text”})

此索引允许在具有字符串内容的所有字段上进行文本搜索。如果不清楚要包含在文本索引中的哪些字段或用于临时查询

通配符文本索引可以是复合索引的一部分。例如,以下内容在字段nane和通配符说明符上创建复合索引,例如:

db.collection.createIndex({name:1,“$**,”text”})

排序操作无法从text索引获取排序顺序,即使是复合文本索引也是如此 ; 即排序操作不能使用文本索引中的排序。

8.唯一索引

只要指定了某个field是唯一的,那么在同一个集合中就不允许存在相同的field值,MongoDB默认创建的唯一索引就是_id。
唯一索引一般是这样创建的:

db.members.createIndex( { "user_id": 1 }, { unique: true } )

单索引创建唯一索引,如:

db.persons.createIndex({name:1},{unique:true})

复合键创建唯一索引,如:

db.persons.createIndex({name:1,email:1},{unique:true})

9.查看索引:

MongoDB提供的查看索引信息的方法:

  • getIndexes()方法可以用来查看集合的所有索引,
  • getIndexKeys()方法查看索引键。
  • totalIndexSize()查看集合索引的总大小,
  • getIndexSpecs()方法查看集合各索引的详细信息db

MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字段),_id是文档唯一的标识,为了保证能根据文档id快递查询文档,MongoDB默认会为集合创建_id字段的索引。

例1: getIndexes()的用法

> db.exams.getIndexes()
[
        {
                "v" : 2,                 //索引版本
                "key" : {                //索引的字段及排序方向  
                        "_id" : 1        //根据_id字段升序索引
                },
                "name" : "_id_",          //索引的名称
                "ns" : "Steam.exams"      //集合名
        }
]

例2:getIndexKeys()的用法

> db.exams.getIndexKeys()
[ { "_id" : 1 } ]

例3:totalIndexSize()的用法

> db.exams.totalIndexSize(65536

10.删除索引:

不再需要的索引,我们可以将其删除,mongodb提供两种删除索引的方法:

  • dropIndex()方法用于删除指定的索引
  • dropIndexes()方法用于删除全部的索引

例1:dropIndex()的用法

> db.users.dropIndex()

11.注意:

11.1 MongoDB中索引是大小写敏感的。

当更新对象是,只有在索引上的这些key发生变化时才会更新。着极大地提高了性能。当对象增长了或者必须移动时,所有的索引必须更新,这回很慢 。

索引信息会保存在system.indexes 集合中,运行 db.system.indexes.find() 能够看到这些示例数据。

索引的字段的大小有最大限制,目前接近800 bytes. 可在大于这个值的字段上建立索引是可以的,但是该字段不会被索引,这种限制在以后的版本中可能被移除。

11.2 索引的性能

索引使得可以通过关键字段获取数据,能够使得快速查询和更新数据。

但是,必须注意的是,索引也会在插入和删除的时候增加一些系统的负担。往集合中插入数据的时候,索引的字段必须加入到B-Tree中去。

因此,索引适合建立在读远多于写的数据集上,对于写入频繁的集合,在某些情况下,索引反而有副作用。不过大多数集合都是读频繁的集合,所以集合在大多数情况下是有用的。

11.3 使用sort()而不需要索引

如果数据集合比较小(通常小于4M),使用sort()而不需要建立索引就能够返回数据。在这种情况下,做好联合使用limit()和sort()。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值