索引(Index)


  • 註冊用戶

    1. 前言

    • MongoDB 的索引几乎与传统的关系型数据库索引一模一样,绝大多数优化关系型数据库索引的方法也同样适用于MongoDB。

    2. 索引简介

    • 数据库的索引和书本中的索引作用是一致的,都是为了快速查找到自己想要阅读的内容,而不用翻遍整本书。数据库建立了索引理论上可以让查询速度得到大量提升,因为在索引中找到条目以后就可以直接跳转到目标文档的位置,而不用扫描整个表或集合。

    3. 索引的目的

    • 创建数据库的索引就像确定如何去组织一本书的索引一样,当我们确定需要建立索引的时候,应该至少先预计一下今后会做何种查询,还有哪些内容是需要快速查询。例如:所有的查询都包括 “ID” 键,那么这个时候就至少需要创建一个关于"ID" 的索引。如果要查询 "UserID" ,则无需对 "UserName" 建立索引,因为不会对其进行查询。

    4. 索引的使用

    • 当查询中仅使用一个键时,可以对该键建立索引,以提高查询速度。比如我们需要对集合person中name进行查找:
    //查询 name 为 jason 的文档
    > db.person.find({ "name" : "jason" })
    { "_id" : ObjectId("5821757bf28fbb3e3516eeaa"), "name" : "jason", "age" : 30 }
    { "_id" : ObjectId("582182b0f28fbb3e3516eead"), "name" : "jason", "age" : 28 }
    //使用ensureIndex方法对 "name" 建立索引
    > db.person.ensureIndex({ "name" : 1})
    {
            "createdCollectionAutomatically" : false,
            "numIndexesBefore" : 1,
            "numIndexesAfter" : 2,
            "ok" : 1
    }
    
    • 同一个集合,同样的索引只需要创建一次,反复创建没有任何意义。
    • 对某个键建立的索引会加速对该键的查询,但是对于其他的查询可能没有帮助,即使是查询中包含了被索引的键。例如下面的查询就不会有任何性能提升。
    //查询 age 排序中包含已创建索引的键 name
    > db.person.find({ "age" : { $gt:20 } }).sort({ "age" : 1,"name" : 1})
    
    • 因为没有对 age 建立索引,要找到想要的年龄服务器就必须进行全表扫描,这个就相当于在没有索引的书中找内容,就需要从第一页找到最后一页。当集合非常大的时候查询会变慢,所以要尽量避免全表扫描情况的发生。对于上面的查询应该对 name 和 age 都创建索引:
    //对 name&age 同时建立索引
    db.person.ensureIndex({ "name" : 1, "age" : 1})
    {
            "createdCollectionAutomatically" : false,
            "numIndexesBefore" : 2,
            "numIndexesAfter" : 3,
            "ok" : 1
    }
    

    5. 索引管理

    • 索引信息存储在每个数据库的系统集合 system.indexes 中,查看这个集合可以使用 db.system.indexes.find() 这个方法。这是一个系统级的集合,不能直接对集合进行插入或删除文档操作。只能通过 ensureIndex 和 dropIndexes 的方式进行操作。
    > db.system.indexes.find()
    { "v" : 1, "key" : { "_id" : 1 }, "ns" : "bitest.person", "name" : "_id_" }
    
    • system.namespaces集合也记录了索引的名字,如果查看这个集合会发现每个集合至少有两个文档与之对应,一个对应集合本身,一个对应集合包含的索引。如果存在复合索引,system.namespaces会增加一条{"name" : "bitest.person.$name_1_age_1"} 这样的文档。
    > db.system.namespaces.find()
    { "name" : "bitest.person"}
    { "name" : "bitest.person.$_id_" }
    { "name" : "bitest.person.$name_1" }
    { "name" : "bitest.person.$name_1_age_1" }
    
    • 随着应用数据的积累或集合结构的改变,老的索引会出现效率低下的问题,修改索引页也是不可避免了。我们可以随时通过ensureIndex函数为集合添加索引。
    //对age 添加索引
    > db.person.ensureIndex({ "age" : 1})
    
    • 对于没有用的索引,我们应尽快删除,因为索引会影响数据库的增删改的效率。利用集合的dropIndex(indexName)删除一个集合上的特定索引。我们使用这个函数的正确步骤应该是先通过查询system.indexes确认索引的名称,然后再删除。
    //查找索引并删除
    > db.system.indexes.find({"ns":"bitest.person"});  
    { "v" : 1, "key" : { "_id" : 1 }, "ns" : "bitest.person", "name" : "_id_" }  
    { "v" : 1, "key" : { "name" : 1 }, "ns" : "bitest.person", "name" :  "name_1" }  
    { "v" : 1, "key" : { "age" : 1 }, "ns" : "bitest.person", "name" :  "age_1" }  
    { "v" : 1, "key" : { "name" : 1, "age" : 1 }, "ns" : "bitest.person", "name" : "name_1_age_1" }   
    > db.person.dropIndex("name_1_age_1");  
    { "nIndexesWas" : 3, "ok" : 1 }  
    
    • 还有一个函数dropIndexes,它不接受任何参数,会直接删除集合中全部索引,这个函数要谨慎使用。

    6. 地理空间索引

    • 随着移动设备的广泛应用,LBS(基于位置服务) 变得越来越流行。目前有很多APP可以查询所在位置附近的一些场所,为了提升这种查询的速度,MongoDB 为坐标平面查询提供了专门的索引,称为地理空间索引。

    6.1 使用地理空间索引目的

    • 假设我们要找到当前位置附近最近的银行,就需要创建一个专门的索引来提高基于这种查询的效率,因为位置查询需要搜索经纬度两个纬度。
    • 地理空间索引同样可以使用 ensureIndex 创建,唯一变化是参数由 1、-1 变为2d 。
    • 由于地理空间索引默认值的范围为(-180~180),我们将测试数据也放在这个范围内
    //查询准备好的测试位置信息文档
    > db.testmap.find()
    { "_id" : ObjectId("582e9e3ef28fbb3e3516eeb7"), "bank" : "addr1", "gps" : [ -180, 100 ] }
    { "_id" : ObjectId("582e9e3ff28fbb3e3516eeb8"), "bank" : "addr2", "gps" : [ 100, 110 ] }
    { "_id" : ObjectId("582e9e3ff28fbb3e3516eeb9"), "bank" : "addr3", "gps" : [ 110, 120 ] }
    { "_id" : ObjectId("582e9e3ff28fbb3e3516eeba"), "bank" : "addr4", "gps" : [ 120, 130 ] }
    { "_id" : ObjectId("582e9e3ff28fbb3e3516eebb"), "bank" : "addr5", "gps" : [ 130, 140 ] }
    { "_id" : ObjectId("582e9e3ff28fbb3e3516eebc"), "bank" : "addr6", "gps" : [ 140, 150 ] }
    { "_id" : ObjectId("582e9e3ff28fbb3e3516eebd"), "bank" : "addr7", "gps" : [ 150, 160 ] }
    { "_id" : ObjectId("582e9e3ff28fbb3e3516eebe"), "bank" : "addr8", "gps" : [ 160, 170 ] }
    { "_id" : ObjectId("582e9e41f28fbb3e3516eebf"), "bank" : "addr9", "gps" : [ -170, 180 ] }
    

    6.2 创建地理空间索引

    > db.testmap.ensureIndex({"gps":"2d"})
    {
            "createdCollectionAutomatically" : false,
            "numIndexesBefore" : 1,
            "numIndexesAfter" : 2,
            "ok" : 1
    }
    

    6.3 查询地理空间

    • 地理空间查询可以通过find或使用数据库命令,这里我们需要使用“$near”查询操作符。
    //查询(-100,120) 的地址,显示顺序为最接近在前
    > db.testmap.find({"gps" : {"$near" : [-100,120]} })
    { "_id" : ObjectId("582ea424f28fbb3e3516eec0"), "bank" : "addr1", "gps" : [ -180, 100 ] }
    { "_id" : ObjectId("582ea425f28fbb3e3516eec8"), "bank" : "addr9", "gps" : [ -170, 180 ] }
    { "_id" : ObjectId("582ea424f28fbb3e3516eec1"), "bank" : "addr2", "gps" : [ 100,110 ] }
    { "_id" : ObjectId("582ea424f28fbb3e3516eec2"), "bank" : "addr3", "gps" : [ 110,120 ] }
    { "_id" : ObjectId("582ea424f28fbb3e3516eec3"), "bank" : "addr4", "gps" : [ 120,130 ] }
    { "_id" : ObjectId("582ea424f28fbb3e3516eec4"), "bank" : "addr5", "gps" : [ 130,140 ] }
    { "_id" : ObjectId("582ea424f28fbb3e3516eec5"), "bank" : "addr6", "gps" : [ 140,150 ] }
    { "_id" : ObjectId("582ea424f28fbb3e3516eec6"), "bank" : "addr7", "gps" : [ 150,160 ] }
    { "_id" : ObjectId("582ea424f28fbb3e3516eec7"), "bank" : "addr8", "gps" : [ 160,170 ] }
    

    7. 总结

    • 索引是数据库查询提升效率的利器,对于任何数据库都是如此。我们在实际工作中要合理的运用索引来提升系统效率。

  • 註冊用戶

    学习了,谢谢分享!o(*≧▽≦)ツ


登录后回复
 

与 萌阔论坛 的连接断开,我们正在尝试重连,请耐心等待