`
onelark
  • 浏览: 32300 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

MongoDB权威指南(4)- 索引

 
阅读更多

Note:mongoDB的索引的工作方式和关系数据库中的索引几乎是一样的。

1.索引简介

假设我们要按单个key查询,如下:

> db.people.find({"username" : "mark"})

对单个的key进行查询的时候,我们可以在这个key上建立索引来提高查询速度。使用ensureIndex方法建立索引如下:

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

一个索引只需创建一次,重复创建相同的索引没有任何效果。

一个key上建立的索引会使对这个key的查询速度提高,除此之外就没有效果了,即使是查询包含这个key,如:

> db.people.find({"date" : date1}).sort({"date" : 1"username" : 1})

这个查询里,服务器必须遍历整个collction来找到日期符合的记录,这个过程叫做table scan(全表扫描),一般情况下你都会尽量避免

table scan,因为它对大型的collection运行非常缓慢。作为一条经验规则,你需要给它创建一个索引,包含了查询中用到的所有key的一个索引。

> db.ensureIndex({"date" : 1"username" : 1})

传递给ensureIndex方法的document参数和sort方法的参数是一样的,它是一组key/value对,值可能是1或-1,代表索引进行的方向。

如果索引里只有一个key,方向就无所谓了,如果索引里有多个key,那么你就得考虑索引的方向问题。假设我们有下边的一些用户:

"_id" : ..., "username" : "smith""age" : 48"user_id" : 0 }
"_id" : ..., "username" : "smith""age" : 30"user_id" : 1 }
"_id" : ..., "username" : "john""age" : 36"user_id" : 2 }
"_id" : ..., "username" : "john""age" : 18"user_id" : 3 }
"_id" : ..., "username" : "joe""age" : 36"user_id" : 4 }
"_id" : ..., "username" : "john""age" : 7"user_id" : 5 }
"_id" : ..., "username" : "simon""age" : 3"user_id" : 6 }
"_id" : ..., "username" : "joe""age" : 27"user_id" : 7 }
"_id" : ..., "username" : "jacob""age" : 17"user_id" : 8 }
"_id" : ..., "username" : "sally""age" : 52"user_id" : 9 }
"_id" : ..., "username" : "simon""age" : 59"user_id" : 10 }

如果我们建立索引{"username" : 1, "age" : -1},mongoDB就会按下边的样子组织用户:

"_id" : ..., "username" : "jacob""age" : 17"user_id" : 8 }
"_id" : ..., "username" : "joe""age" : 36"user_id" : 4 }
"_id" : ..., "username" : "joe""age" : 27"user_id" : 7 }
"_id" : ..., "username" : "john""age" : 36"user_id" : 2 }
"_id" : ..., "username" : "john""age" : 18"user_id" : 3 }
"_id" : ..., "username" : "john""age" : 7"user_id" : 5 }
"_id" : ..., "username" : "sally""age" : 52"user_id" : 9 }
"_id" : ..., "username" : "simon""age" : 59"user_id" : 10 }
"_id" : ..., "username" : "simon""age" : 3"user_id" : 6 }
"_id" : ..., "username" : "smith""age" : 48"user_id" : 0 }
"_id" : ..., "username" : "smith""age" : 30"user_id" : 1 }

首先按名字的升序排列,名字相同的组里按降序排列。这索引会优化按{"username" : 1, "age" :-1}的排序操作,而对{"username" : 1, "age" : 1}

的排序效果就没那么好了,如果我们想优化{"username" : 1, "age" : 1},那就应该按{"username" : 1, "age" : 1}来建立索引,让年龄也升序排列。

对username和age建立的索引同时也会是对username的查询速度提高,通常,如果索引有N个key组成,对其中前边部分的查询速度也会提高。

例如,我们建立了索引{"a" : 1, "b" : 1, "c" : 1, ..., "z" : 1},那么效果上相当于我们也有了{"a" : 1}, {"a" : 1, "b" : 1}, {"a" : 1, "b" : 1, "c" :1}等等。

mongoDB的查询优化器会调整查询条件之间的顺序以利用索引,比如说你要查询{"x" : "foo", "y" : "bar"},而你的索引是{"y" : 1, "x" :1},优化器会自行调整。

索引的不利之处是给插入、更新、删除操作增添了一些负担。

在某些情况下,使用索引也许还不如不用索引。通常,如果查询返回collection里一半甚至更多的记录,那么相比为几乎每个document查找索引及其值,直接使用

全表扫描还更快些。

索引度量? (Scaling Index)

假设我们有个collection存储用户的状态消息,我们想按用户查询每个用户的最新状态,根据我们学到的知识,我们可能会这样建立索引:

> db.status.ensureIndex({user : 1, date : -1})

这个索引会使对user和date的查询速度提高,但实际上并不是最好的选择。按照这个索引,我们的数据可能是下边这个样子:

User 123 on March 132010
User 
123 on March 122010
User 
123 on March 112010
User 
123 on March 52010
User 
123 on March 42010
User 
124 on March 122010
User 
124 on March 112010
...

如果只是这个数据规模,这样子看起来还是不错的,如果程序里有百万千万的用户,每个用户每天都会产生几十条状态更新呢?

如果每个用户的状态消息的索引记录都占用了磁盘空间一页的大小,那么每次进行最新状态查询时,数据都不得不加载另外一个页面进内存。

要是我们使用{date : -1, user : 1}做索引,那么数据库就可以将最近几天的索引保持在内存里,会有更少的页面对换,查询最新状态

也会更快。

对嵌入document的key建立索引

> db.blog.ensureIndex({"comments.date" : 1})

对嵌入的document建立索引和对顶级document建立索引没有差别,两者在组合索引里也可以组合使用。

为排序建立索引

如果对一个未建立索引的key调用sort方法,mongoDB需要取出所有的数据,放入内存然后排序,所以这个大小是有限制的,

如果collection太大,mongoDB就会返回一个错误。建立索引可以避免这个问题,使你可以对任意数量的数据进行排序而不会耗尽内存。

2.唯一索引 

唯一索引保证对于指定的key,collection里每个document中其值都是唯一的。如,要保证用户名都不重复:

> db.people.ensureIndex({"username" : 1}, {"unique" : true})

Note:如果key不存在,索引就会将其值存储为null,如果要再插入一个不含此key的document,插入就会失败,因为已经有了一个

值为null的document。

删除重复

对已有的collection建立唯一索引时,里边也许已经有了重复的值,这会导致索引建立失败,如果你想删掉具有重复值的document,

可以使用dropDups选项,遇到的第一个document被保留,其他的都被删除掉了。

> db.people.ensureIndex({"username" : 1}, {"unique" : true"dropDups" : true})

组合唯一索引

组合唯一索引里的单个key的值可以是重复的,但是所有key的组合必须是唯一的。

3.使用explain和hint

> db.foo.find().explain()

explain方法返回一个document而不是游标本身,这个document包含了用到的索引、统计信息等。

举个例子,对一个无索引的collection执行一个最简单的查询({}),返回64个document,那么explain的输出为

> db.people.find().explain()
{
  "cursor" : "BasicCursor",
  "indexBounds" : [ ],
  "nscanned" : 64,
  "nscannedObjects" : 64,
  "n" : 64,
  "millis" : 0,
  "allPlans" : [
  {
    "cursor" : "BasicCursor",
    "indexBounds" : [ ]
  }
  ]

}
  • "cursor" : "BasicCursor"
    意思是查询没有使用索引
  • "nscanned" : 64
    数据库扫描过的document数量
  • "n" : 64
    返回的结果集的document数量
  • "millis" : 0
    数据库执行查询消耗的毫秒数

现在我们看个稍微复杂点的例子,假设我们在age键上建立了索引,我们要查询年龄为20多岁的用户。

> db.c.find({age : {$gt : 20, $lt : 30}}).explain()
{
  "cursor" : "BtreeCursor age_1",
  "indexBounds" : [
    [{
"age" : 20},{"age" : 30}]
  ],
  "nscanned" : 14,
  "nscannedObjects" : 12,
  "n" : 12,
  "millis" : 1,
  "allPlans" : [
    {
    "cursor" : "BtreeCursor age_1",
    "indexBounds" : [
      [{
"age" : 20},{"age" : 30}]
    ]
  }
  ]
}
  • "cursor" : "BtreeCursor age_1"
    这次不是BasicCursor了,索引是存储在B-Tree的数据结构里,这个查询使用了索引,它是使用了B-Tree类型的游标。
    age_1是索引的名字,有了这个名字我们就可以查询system.indexes collection,获取关于此索引的更多信息。
    > db.system.indexes.find({"ns" : "test.c""name" : "age_1"})
    {
      "_id" : ObjectId("4c0d211478b4eaaf7fb28565"),
      "ns" : "test.c",
      "key" : {
        "age" : 1
      },
      "name" : "age_1"
    }
  • "allPlans" : [ ... ]
    列出了此查询可用的所有的计划。如果我们有多个索引和更加复杂的查询,"allPlans"就会包含所有可能的计划。

让我们看个更复杂点的查询例子,假设我们有一个索引{"username" : 1, "age" : 1}和一个索引{"age" : 1, "username" : 1},那么当我们

查询username和age的时候会发生什么事?实际上这样要依赖于查询。

> db.c.find({age : {$gt : 10}, username : "sally"}).explain()
{
  "cursor" : "BtreeCursor username_1_age_1",
  "indexBounds" : [
    [
      {
        "username" : "sally",
        "age" : 10
      },
      {
        "username" : "sally",
        "age" : 1.7976931348623157e+308
      }
    ]
  ],
  "nscanned" : 13,
  "nscannedObjects" : 13,
  "n" : 13,
  "millis" : 5,
  "allPlans" : [
    {
      "cursor" : "BtreeCursor username_1_age_1",
      "indexBounds" : [
        [
          {
            "username" : "sally",
            "age" : 10
          },
          {
            "username" : "sally",
            "age" : 1.7976931348623157e+308
          }
        ]
      ]
    }
  ],
  "oldPlan" : {
    "cursor" : "BtreeCursor username_1_age_1",
    "indexBounds" : [
      [
        {
          "username" : "sally",
          "age" : 10
        },
        {
          "username" : "sally",
          "age" : 1.7976931348623157e+308
        }
      ]
    ]
  }
}

由于当我们查询的是一个确定的username值和一个age范围值,所以数据库使用的是{"username" : 1, "age" : 1}这个索引,

反过来,如果我们查询的是一个确定的年龄和名字范围,那么数据库就会使用另外的那个索引

> db.c.find({"age" : 14"username" : /.*/}).explain()
{
  "cursor" : "BtreeCursor age_1_username_1 multi",
  "indexBounds" : [
    [
      {
        "age" : 14,
        "username" : ""
      },
      {
        "age" : 14,
        "username" : {
        }
      }
    ],
    [
      {
        "age" : 14,
        "username" : /.*/
      },
      {
        "age" : 14,
        "username" : /.*/
      }
    ]
  ],
  "nscanned" : 2,
  "nscannedObjects" : 2,
  "n" : 2,
  "millis" : 2,
  "allPlans" : [
    {
      "cursor" : "BtreeCursor age_1_username_1 multi",
      "indexBounds" : [
        [
          {
            "age" : 14,
            "username" : ""
          },
          {
            "age" : 14,
            "username" : {
            }
          }
        ],
        [
          {
            "age" : 14,
            "username" : /.*/
          },
          {
            "age" : 14,
            "username" : /.*/
          }
        ]
      ]
    }
  ]
}

如果你发现数据库使用的不是你想用的索引,那么你可以使用hint强制数据库使用你指定的索引。

> db.c.find({"age" : 14"username" : /.*/}).hint({"username" : 1"age" : 1})

指定索引通常是没有必要的,mongoDB有自己的查询优化器,会很聪明地选择使用哪个索引,你只需要关心的是优化器有可用的索引以备选择。

4.索引管理

每个database都有个叫system.indexes的collection,它里边存储了索引的元数据信息,这个collection是保留的,不能进行插入或删除,

只能通过ensureIndex和dropIndexes命令来操作里边的document。system.indexes里包含了每个索引的详细信息,另外还有个叫

system.namespaces的collection列出了索引的名字。查看这collection可以看到,每个collection至少有两条记录,一个是collection本身,

另外的是collection里的每个索引。

建立索引是个耗时耗资源的操作,如果collection的数据量很大,你可以指定background选项来在后台进行工作。

> db.people.ensureIndex({"username" : 1}, {"background" : true})

如果没有使用background选项的话,database就会阻塞所有的请求,知道索引建立完成。

如果你不在需要某个索引,你可以用dropIndexes命令移除它,你可能得先在system.indexes里找到索引的名字,因为各种驱动自动生成

的索引名字各不一样。

> db.runCommand({"dropIndexes" : "foo""index" : "alphabet"})

使用*删除collection的所有的索引

> db.runCommand({"dropIndexes" : "foo""index" : "*"})

 

5.地理空间索引

在ensureIndex方法中使用"2d"做参数而不是1或者-1,建立空间索引

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

gps这个key的值必须是某种成对的值,一个包含两个元素的数组,或者一个有两个key的嵌入的document,下边的例子都是可以的

"gps" : [ 0100 ] }
"gps" : { "x" : -30"y" : 30 } }
"gps" : { "latitude" : -180"longitude" : 180 } }

嵌入的document里边key的名字是任意的,它们的值缺省是从-180到180,方便使用经纬度,如果你要使用其他的单位,可以指定

最大值和最小值

> db.star.trek.ensureIndex({"light-years" : "2d"}, {"min" : -1000"max" : 1000})

地理空间索引可以通过两种方式来使用,一是普通的find查询,另外是作为数据库命令。

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

和下边的使用geoNear命令进行的查询等价

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

mongoDB还允许你使用一个shape来查找document,为了查找shape里所有的点,我们可以使用"$within"条件操作符,使用"$box"

定义一个矩形

> db.map.find({"gps" : {"$within" : {"$box" : [[1020], [1530]]}}})

"$box"的值是含两个元素的数组,第一个是左边的Y值小的顶点,第二个是右边的Y值大的顶点。(大概就是这个意思,因为一般地理坐标系统

中,Y轴是向上的,而我们的屏幕坐标中,原点在左上角,Y轴是向下的,数据库里仅仅是数据)

同样,你也可以找到一个圆里边的所有点,$center的第一个元素是圆心,第二个元素是半径

> db.map.find({"gps" : {"$within" : {"$center" : [[1225], 5]}}})

组合空间索引

> db.ensureIndex({"location" : "2d""desc" : 1})

如果你要查询最近的咖啡馆

> db.map.find({"location" : {"$near" : [-7030]}, "desc" : "coffeeshop"}).limit(1)
{
  "_id" : ObjectId("4c0d1348928a815a720a0000"),
  "name" : "Mud",
  "location" : [x, y],
  "desc" : ["coffee""coffeeshop""muffins""espresso"]
}
分享到:
评论

相关推荐

    MongoDB权威指南中文版.pdf

    但根据文件标题《MongoDB权威指南中文版.pdf》和描述内容,我可以推测读者想要了解的是关于MongoDB数据库系统的一些基础知识和概念。 MongoDB是一款流行的开源文档导向数据库系统(NoSQL),它以其高性能、高可用性...

    MongoDB权威指南-中文版.pdf

    通过阅读《MongoDB权威指南》这本书,你可以深入了解MongoDB的原理、安装与配置、数据模型设计、备份与恢复策略、性能调优、安全性等方面的知识,从而更好地运用MongoDB来解决实际问题。无论你是初学者还是经验丰富...

    MongoDB权威指南.pdf

    英文版:MongoDB: The Definitive Guide内容简介《MongoDB权威指南》是一本广受好评的MongoDB方面的图书。与传统的关系型数据库不同,MongoDB是一种面向文档的数据库。书中介绍了面向文档的存储方式及利用MongoDB的...

    MongoDB权威指南(中英文两版)

    在数据库管理系统中,索引是提高查询性能的关键,"MongoDB权威指南"也深入探讨了MongoDB的索引类型,如单字段索引、复合索引、地理空间索引等,以及如何根据业务需求创建和优化索引。书中还介绍了聚合框架,这是一个...

    MongoDB权威指南 中文版高清PDF

    《MongoDB权威指南》是一本深入解析该技术的专业书籍,旨在帮助读者全面理解和掌握MongoDB的使用。 本书首先从数据库的基础概念入手,介绍了NoSQL数据库与传统关系型数据库的区别,阐述了MongoDB作为文档数据库的...

    MongoDB 权威指南.pdf

    ### MongoDB权威指南知识点详解 #### 一、MongoDB概述 MongoDB是一款开源的NoSQL数据库系统,采用BSON(Binary JSON)格式存储文档数据。它支持动态查询、高可用性及横向扩展等特性,适用于大数据量的应用场景。...

    MongoDB权威指南 中文版

    ### MongoDB权威指南知识点总结 #### 一、MongoDB概述 - **定义与特点**:MongoDB是一种基于分布式文件存储的数据库系统,属于NoSQL数据库的一种。它将数据以BSON(Binary JSON)格式存储,这种格式类似于JSON文档...

    MongoDB权威指南中文版高清版带书签PDF

    在《MongoDB权威指南》这本书中,作者详细地介绍了MongoDB的基础知识、安装配置、数据模型、查询语言、操作管理以及高级特性。以下是对这本书中核心知识点的概述: 1. **MongoDB基础知识**:MongoDB是NoSQL数据库的...

    MongoDB权威指南 中文第2版-01卷

    《MongoDB权威指南(第2版)》这一版共分为六部分,涵盖开发、管理以及部署的各个方面。一部分展示MongoDB基础知识、核心概念。二部分介绍使用MongoDB进行开发,包括索引的概念以及各种特殊索引和集合的用法等。三部分...

    MongoDB权威指南(中文版)

    《MongoDB权威指南》是一本全面深入探讨MongoDB数据库系统的专著,中文版的发布使得国内用户能够更方便地学习和理解这一强大的NoSQL数据库。MongoDB作为一种分布式文档型数据库,近年来在处理大规模数据存储和高并发...

    MongoDB权威指南中文版

    《MongoDB权威指南中文版》第1章 简介 第2章 入门 第3章 创建、更新及删除文档 第4章 查询 第5章 索引 第6章 聚合 第7章 进阶指南 第8章 管理 第9章 复制 第10章 分片 第11章 应用举例 附录A 安装MongoDB 附录B ...

    MongoDB权威指南(中文版)高清

    856.4.2 例2:网页分类 876.4.3 MongoDB和MapReduce 87第7章 进阶指南 917.1 数据库命令 917.1.1 命令的工作原理 927.1.2 命令参考 937.2 固定集合 957.2.1 属性及用法 967.2.2 创建固定集合 967....

    mongodb权威指南(第二版)

    综上所述,根据标题、描述、标签和部分内容所透露的信息,可以构建关于MongoDB权威指南(第二版)的知识点。这些知识点不仅涵盖了MongoDB的关键概念和操作,还涉及到了该书的出版信息和更新情况,这对于数据库管理员...

    Mongo DB 权威指南(中文版)

    ### MongoDB权威指南(中文版)知识点总结 #### 一、MongoDB概述 1. **定义与特点**:MongoDB是一款开源的文档型数据库系统,它使用JSON格式的文档存储数据,支持动态模式,使得开发人员可以轻松地进行数据操作。...

    MongoDB权威指南第2版中文版带目录

    《MongoDB权威指南第2版》是深入理解和应用MongoDB的重要参考资料,书中详细介绍了MongoDB的基础知识、高级特性和最佳实践。 本书首先会讲解MongoDB的基本概念,包括数据库、集合、文档以及数据模型的设计。MongoDB...

    MongoDB权威指南高清中英文两版PDF

    这份"MongoDB权威指南高清中英文两版PDF"涵盖了MongoDB的基础到高级概念,是学习和掌握这一数据库系统的重要参考资料。 首先,中文版与英文版的结合使得无论对于初级还是高级用户,都能根据自己的语言习惯进行学习...

Global site tag (gtag.js) - Google Analytics