风-fmgao

导航

MongoDB索引

简介

索引就是用来加速查询的。数据库索引与书籍的索引类似:有了索引就不需要翻遍整本书,数据库则可以直接在索引中査找,使得查找速度能提高几个数量级。在索引中找到条目以后,就可以直接跳转到目标文档的位置。让这个比喻走个极端,可以说创建数据库索引就像确定如何组织书的索引一样。但你的优势是知道今后会做何种査询,以及哪些内容需要快速査找。索引能够提升查询效率.没有索引,MongoDB必须扫描集合中的所有文档才能找到匹配查询语句文档,索引是一种特殊的数据结构,将一小块数据集保存为容易遍历的形式.索引能够存储某种特殊字段或字段的值,按照索引指定的方式可以将字段值进行排序,对于添加的每一个索引,每一次插入更新,删除都将消耗更多的时间,因为数据发生改变的时候不仅仅是更新文档,还有更新集合上的所有索引.MongoDB限制每个集合上的最多有64个索引,使用索引键对文档进行排序时可以提升排序效率,索引时按照一定顺序排列的,,索引才有效果,索引类型有很多包括 单字段索引、复合索引、多支索引、地理空间索引、文本索引等等.

创建索引

> db.collection.ensureIndex(<keys>,<options>)  #3.0版本

 

keys 希望创建索引的名称和排序方式1表示升序,-1表示降序排列,格式{key:1, key-1}如果时基于多个索引键进行排序,要特别注意索引的排序方向,相同的排序方向可以提升排序效率,单个键排序时没有影响。

options:可选参数,表示建立索引的设置:

参数数据类型默认值功能
background Boolean false 后台建立索引,以便创建索引时不阻止其他数据库活动
unique Boolean false 创建唯一索引
name String   指定索引名称,如果未指定,MongoDB会生成一个索引字段的名称和排序顺序串联
partialFilterExpression document   如果指定MongoDB指挥满足过滤表达式的记录
sparse Boolean false 对文档中不的存在的字段数据不启用索引
expireAfterSeconds integer   指定索引的过期时间
storageEngine     document类型允许用户配置索引的额存储引擎

删除索引

方式一

删除当前集合中的所有索引(_id上默认索引除外)

db.collection.dropIndexes()

 方式二

db.collection.dropIndex(index)

 

在索引上找到条目之后,就可以直接跳到目标文档的位置

在user中有5个文档,其中存储信息如下

{ "_id" : ObjectId("5c0df99fbc6d47cbcdb55fd0"), "name" : "jack", "age" : 19 }
{ "_id" : ObjectId("5c0df9abbc6d47cbcdb55fd1"), "name" : "rose", "age" : 20 }
{ "_id" : ObjectId("5c0df9b0bc6d47cbcdb55fd2"), "name" : "jack", "age" : 18 }
{ "_id" : ObjectId("5c0df9c3bc6d47cbcdb55fd3"), "name" : "tony", "age" : 21 }
{ "_id" : ObjectId("5c0df9cdbc6d47cbcdb55fd4"), "name" : "adam", "age" : 18 }

 如果有个查询

> db.user.find({age:18})

 

这个时候需要遍历所有的文档,查询所有的文档,根据位置信息读出文档,对比age字段是否为18。当然入股偶只有5个文档,全表扫描的开销并不大,简单如果集合文档数量到百万,千万或者上亿的时候,对集合进行去哪表扫描的开销时非常大的,一个查询耗费数十秒甚至几分钟都可能。

如果想加速

> db.user.find({age:18})

 

可以对user表的age字段孙银

按照age字段创建升序索引

> db.user.createIndex({age:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

 

建立索引之后,MongoDB会额外存储一份按照ae字段升序排序的索引数据,索引通常采用类似于btree的结构持久化存储

以保证从索引里快速找出某个age值对应的位置信息,然后根据位置信息就能读取出对应的文档。

简单的说,索引就是将文档按照某个(或者某些)字段顺序组织起来,以便能根据该字段高效的查询。

唯一索引

MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字端),_id时文档的唯一表示,为了保证能够根据文档id快速查询文档,MongoDB默认会为集合创建_id的字段索引,唯一索引可以确保集合的每一个文档的指定键都有唯一值。如果想保留文档的“name”键都有不一样的值,创建一个唯一索引就好了:

db.user.ensureIndex({name:1},{unique:true})

 

由于insert并不健查文档时否查如果,为了避免插入的文档包含与唯一键重复i的值,可能要用安全插入才能满足要求。这样,在插入文档时会看到存在重复键的错误的提示。

消除重复

当为已有的索引创建索引,可能有些之已经有重复了。这时候创建所以会失败,我们希望将所有包含重复值的文档都删掉。dropDups选项就可以奥六发现的第一个文档,二删除接下来有重复的键值文档,如“name”键重复的索引值

db.user.ensureIndex({name:1},{unique:true,dropDups:true})

 通常做开发不建议删除操作。

复合唯一索引

符合索引就是建立在多个字段上的索引 例如:

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

 

上述符合索引由“age” “name” 组成的索引

复合唯一索引就是允许单个的键的值相同,但是所有键的组合之组合起来的不同索引

{ "age" : 2, "name" : 1 }
{ "age" : 2, "name" : 2 }
{ "age" : 2, "name" : 3 }

 所有的“age”键都相同,但是“name“键的值不同,如果尝试再次插入

{ "age" : 2, "name" : 1 }

 数据库会体时存在重复键的错误。

索引管理

索引的元信息存储在每个数据库的system.indexes集合中。这是一个保留集合不能对其插入或者删除文档。操作只能通过ensureIndex或者droplndexes进行。

查询索引明细

> db.user.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "han.user"
        },
        {
                "v" : 2,
                "key" : {
                        "age" : 1
                },
                "name" : "age_1",
                "ns" : "han.user"
        },
        {
                "v" : 2,
                "key" : {
                        "age" : 1,
                        "name" : 1
                },
                "name" : "age_1_name_1",
                "ns" : "han.user"
        },
        {
                "v" : 2,
                "key" : {
                        "age" : 2,
                        "name" : 1
                },
                "name" : "age_2_name_1",
                "ns" : "han.user"
        }
]

 查询索引的大小

> db.user.totalIndexSize()
118784(单位:字节)
>

 修改索引

MongoDB没有单独的修改索引方法,如果要修改某个索引,需要先删除旧的索引,再创建新的索引,

随着数据量的增长你会发现某个collection需要修改索引或者增加索引,此时创建索引就会很费力,同时也比较消耗性能。创建索引时MongoDB默认是阻塞式,会让索引建立更快,任何请求都不能相应。可以使用{ background:True } 选项再后台完成,同也可能正常处理请求。不过这种方式也会造成请求的响应很慢,如果再非紧急的情况下,最好晚上业务量少的时候统一处理。

地理空间索引

还有一种査询变得越来越流行(尤其是随着移动设备的出现):找到离当前位置最近的N个场所。MongoDB为坐标平面査询提供了专门的索引,称作地理空间索引。假设要找到给定经纬度坐标周围最近的咖啡馆,就需要创建一个专门的索引来提髙这种査询的效率,这是因为这种査询需要搜索两个维度。地理空间索引同样可以由ensurelndex来创建,只不过参数不是1或者-1,而是2d”:

db.map.ensurelndex ({ "gps":"2d"})

 “gps”键的值必须是某种形式的一对值:一个包含两个元素的数组或是包含两个键的内嵌文档。下面这些都是有效的:

{ "gps" : [0, 100 ] }
{ "gps" : { "X" : -30, "y" : 30 } }
{ "gps,1 : { "latitude” : -180, "longitude” : 180 } }

 至于键名可以随意,例如

"gps" : {"foo" : 0, "bar" : 1}}

 

也是可以的。

默认情况下,地理空间索引假设值的范围是-180 180 (对经纬度来说很方便)。要是想用其他值,可以通过ensurelndex的选项来指定最大最小值:

db.star.trek. ensurelndex ({" light-years” : ,,2d,'}, {"min" : -1000,"max" : 1000})

 这样就创建了一个2000光年见方的空间索引。
地理空间査询以两种方式进行,即普通査询(用find)或者使用数据库命令。find的方式与一般的查询差别不大,只不过用了”$near”。需要两个目标值的数组作为参数:

db.map.find{ {"gps1':{”$near" : [40,-73]}})

 这会按照离点(40, -73)由近及远的方式将map集合的所有文档都返回。在没有指定limit的值时,默认是100个文档。要是不需要这么多结果,就应该设置一个少点的值以节约资源。例如,下面的例子将返回离(40, -73)最近的10个文档。

db.map.find({ngps" : {"$near" :[40, -73]}}).limit(10)

 也可以用geoNear来完成相同的操作:

db.runCommand({geoNear : "map", near : [40, -73], num : 10});

 

geoNear还会返回每个文档到査询点的距离。这个距离是以你插入的数据为单位的,如果按照经纬度的角度插入,则距离就是经纬度。find与”$near”的组合不会给出距离,但若是结果大于4MB,这是唯一的选择

MongoDB不但能找到靠近一个点的文档,还能找到指定形状内的文档。做法就是将原来的nnearn

within”。n$within”获取数量不断增加的形状作为参数。若查看地理空间索引的联机帮助文档(http://www.mongodb.org/display/DOCS/Geospatial+ Indexing),可以找到最新的形状列表。有两个选项:你可以査询矩形和圆形内的所有点。

对于矩形,使用”$box”选项:

db.map.find({HgpsM : {"$withinn : {"$box" : [[10, 20], [15, 30]]}}})

 

”$box”参数是两个元素的数组,第一个元素指定了左下角的坐标,第二个指定右上角的坐标。

同样,也可以用“”$center”来找到圆形内部的所有点,只不过参数变成了圆心和半径:

db.map.find ({"gps" : {"$within” : {"$center" : [[12,25], 5] }}})

 复合地理空间索引

应用经常要找的东西往往不只是一个地点。例如,用户要找出周围所有的咖啡店或者披萨店。将地理空间索引与普通索引组合起来就可以满足这种需求。例如,要査询”location”和”descn,就可以这样创建索引:

db.ensurelndex ({ " location11 : "2d", "desc" : 1})

 然后就能很快找到最近的咖啡馆了:

> db.map.find {{location: {"$near" : [-70, 30]}, "desc":
"coffeeshop”}).limit(1)
{
	"_id” : Objectld(”4c0dl348928a815a720a0000"),
	"name" : "Mud",
	"location" : [x, y],
	"desc" : ["coffee","coffeeshop", "muffins", "espresso"]
}

 地球不是二维平面

MongoDB的地理空间索引假设索引内容是在一个平面上的。这就意味着对于球体,比如地球,它并不是十分精确,尤其是在极地区域。具体来说,两条经线之间纬线的长度在赤道和在育空地区1是大不一样的,后者要短得多。可以用不同的投影手段将地球映射到二维平面,当然它们的精度和相应的复杂度各不相同。

posted on 2019-02-21 09:56  风-fmgao  阅读(194)  评论(0编辑  收藏  举报