• 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是大不一样的,后者要短得多。可以用不同的投影手段将地球映射到二维平面,当然它们的精度和相应的复杂度各不相同。

  • 相关阅读:
    java多线程之happens-before
    三次握手和四次挥手
    数据库操作事物的四大特性以及MySQL数据库的四种隔离级别
    网易云易盾中标浙报反作弊服务 助力浙江新闻App健康发展
    值得细读!如何系统有效地提升Android代码的安全性?
    逻辑编程入门--clojure.core.logic
    React server rendering —— 网易美学主站同构实录
    致传统企业朋友:不够痛就别微服务,有坑 (1)
    致传统企业朋友:不够痛就别微服务,有坑 (2)
    亲近用户—回归本质
  • 原文地址:https://www.cnblogs.com/fmgao-technology/p/10410603.html
Copyright © 2020-2023  润新知