MongoDB 复合索引

MongoDB支持复合索引,即将多个键组合到一起创建索引。该方式称为复合索引,或者也叫组合索引,该方式能够满足多键值匹配查询使用索引的情形。其次复合索引在使用的时候,也可以通过前缀法来使用索引。MongoDB中的复合索引与关系型数据库基本上一致。在关系型数据库中复合索引使用的一些原则同样适用于MongoDB。本文主要描述MongoDB复合索引。

一、复合索引相关概述

1、复合索引创建语法
        db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )
        同创建单键(列)索引一样,索引创建时需要指定每一个键索引的顺序
        多个键直接用逗号分隔
        索引创建语法可以参考:http://blog.csdn.net/leshami/article/details/53541978

2、复合索引的一些特性
        复合索引可以支持要求匹配多个键的查询
        复合索引每一个键的顺序非常重要,这将决定该索引在查询过程中能否被使用到
        复合索引支持前导(缀)列索引查询
        不能够创建基于哈希索引类型的复合索引
        任意复合索引字段不能超过31个

二、复合索引示意图

如下图所示,在集合的userid以及score列上创建一个复合索引,其中userid为升序,score为降序

三、复合索引示例

1、演示环境

> db.version()
3.2.10
> db.example.find({},{"_id":0})
{ "id" : 1, "ename" : "leshami", "blog" : "http://blog.csdn.net/leshami", "name" : "leshami" }

演示集合数据,可以参考:http://blog.csdn.net/leshami/article/details/52672310

//查看任意的一个文档
> db.persons.find().limit(1).pretty()
{
        "_id" : ObjectId("5812cbaaa129eed14b46458d"),
        "name" : "robinson.cheng",
        "age" : 25,
        "email" : "robinson.cheng@qq.com",
        "score" : {
                "c" : 89,
                "m" : 96,
                "e" : 87
        },
        "country" : "USA",
        "books" : [
                "JS",
                "C++",
                "EXTJS",
                "MONGODB"
        ]
}

2、创建复合索引

//如下示例,我们在集合persons上的name及age键上创建复合索引,且2个都为升序
> db.persons.createIndex({name:1,age:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

//在上面的示例中索引首先会按照name的值升序进行排列
//其次是age键,在name之后也按照升序排列

//下面过滤条件仅使用一个name键来查看执行计划
> db.persons.find({name:"robinson.cheng"}).explain()
{
   ......
   "stage" : "FETCH",
   "inputStage" : {
           "stage" : "IXSCAN",  //使用索引扫描
           "keyPattern" : {
                   "name" : 1,
                   "age" : 1
           },
           "indexName" : "name_1_age_1",
             ......
           "direction" : "forward",
           "indexBounds" : {
                   "name" : [
                           "[\"robinson.cheng\", \"robinson.cheng\"]"
                   ],
                   "age" : [
                           "[MinKey, MaxKey]"
  .........
 "ok" : 1
}

//下面过滤条件仅使用name及age键来查看执行计划
> db.persons.find({name:"robinson.cheng",age:25}).explain()
{
                 .........
                 "stage" : "FETCH",
                 "inputStage" : {
                         "stage" : "IXSCAN",  //使用索引扫描
                         "keyPattern" : {
                                 "name" : 1,
                                 "age" : 1
                         },
                         "indexName" : "name_1_age_1",
                          .........
                         "direction" : "forward",
                         "indexBounds" : {
                                 "name" : [
                                         "[\"robinson.cheng\", \"robinson.cheng\"]"
                                 ],
                                 "age" : [
                                         "[25.0, 25.0]"
          ...........
 "ok" : 1
}

//下面过滤条件仅使用name及age键来查看执行计划,但是将age键放在name键之前
> db.persons.find({age:25,name:"robinson.cheng"}).explain()
{
 "queryPlanner" : {
      .....
         "winningPlan" : {
                 "stage" : "FETCH",
                 "inputStage" : {
                         "stage" : "IXSCAN",  //使用索引扫描
                         "keyPattern" : {
                                 "name" : 1,
                                 "age" : 1
                         },
                         "indexName" : "name_1_age_1",
                          ...........
                         "direction" : "forward",
                         "indexBounds" : {
                                 "name" : [
                                         "[\"robinson.cheng\", \"robinson.cheng\"]"
                                 ],
                                 "age" : [
                                         "[25.0, 25.0]"
       ........
 "ok" : 1
}

//下面单独基于age键作为过滤条件进行查询
> db.persons.find({age:25}).explain()
{
         ................
                "winningPlan" : {
                        "stage" : "COLLSCAN",  //此处为使用集合扫描方式
                        "filter" : {
                                "age" : {
                                        "$eq" : 25
                                }
                        },
                        "direction" : "forward"
                },
                "rejectedPlans" : [ ]
         ..............
        "ok" : 1
}

3、复合索引与排序

复合索引创建时按升序或降序来指定其排列方式。对于单键索引,其顺序并不是特别重要,因为MongoDB可以在任一方向遍历索引
对于复合索引,按何种方式排序能够决定该索引在查询中能否被使用到。

//以下内容基于前面在{name:1,age:1}键上创建的索引来考察这个复合索引在排序时被使用到的场景
//基于{name:1,age:1}的排序
> db.persons.find().sort({name:1,age:1}).explain()
{
    "queryPlanner" : {
            ....
            "winningPlan" : {
                    "stage" : "FETCH",
                    "inputStage" : {
                            "stage" : "IXSCAN",   //索引扫描
                            "keyPattern" : {
                                    "name" : 1,
                                    "age" : 1
                            },
                            "indexName" : "name_1_age_1",
                             ..........
                            "direction" : "forward",
                            "indexBounds" : {
                                    "name" : [
                                            "[MinKey, MaxKey]"
                                    ],
                                    "age" : [
                                            "[MinKey, MaxKey]"
         ....
    "ok" : 1
}

//基于{name:1,age:-1}的排序
> db.persons.find().sort({name:1,age:-1}).explain()
{
    "queryPlanner" : {
      ....
            "winningPlan" : {
                    "stage" : "SORT",
                    "sortPattern" : {
                            "name" : 1,
                            "age" : -1
                    },
                    "inputStage" : {
                            "stage" : "SORT_KEY_GENERATOR",
                            "inputStage" : {
                                    "stage" : "COLLSCAN", //集合扫描
                                    "filter" : {
                                            "$and" : [ ]
                                    },
                                    "direction" : "forward"
       .........
    "ok" : 1
}

//基于{name:-1,age:1}的排序
> db.persons.find().sort({name:-1,age:1}).explain()
{
   "queryPlanner" : {
           .........
           "winningPlan" : {
           "stage" : "SORT",
           "sortPattern" : {
                   "name" : -1,
                   "age" : 1
           },
           "inputStage" : {
                   "stage" : "SORT_KEY_GENERATOR",
                   "inputStage" : {
                           "stage" : "COLLSCAN",  //集合扫描
                           "filter" : {
                                   "$and" : [ ]
                           },
                           "direction" : "forward"
      .........
   "ok" : 1
}

//基于{age:1,name:1}的排序
> db.persons.find().sort({age:1,name:1}).explain()
{
          ....
         "inputStage" : {
          "stage" : "SORT_KEY_GENERATOR",
          "inputStage" : {
                  "stage" : "COLLSCAN",  //集合扫描
                  "filter" : {
                          "$and" : [ ]
                  },
                  "direction" : "forward"
         ..........
  "ok" : 1
}

//基于{age:-1,name:1}的排序
> db.persons.find().sort({age:-1,name:1}).explain()
{
   ..........
   "inputStage" : {
           "stage" : "SORT_KEY_GENERATOR",
           "inputStage" : {
                   "stage" : "COLLSCAN",    //集合扫描
                   "filter" : {
                           "$and" : [ ]
                   },
                   "direction" : "forward"
   ..........
  "ok" : 1
}

//基于{name:-1,age:-1}的排序
> db.persons.find().sort({name:-1,age:-1}).explain()
{
      .........
      "inputStage" : {
              "stage" : "IXSCAN",      //索引扫描
              "keyPattern" : {
                      "name" : 1,
                      "age" : 1
              },
              "indexName" : "name_1_age_1",
              ............
              "direction" : "backward", //注意,这里的方向为向后扫描
              "indexBounds" : {
                      "name" : [
                              "[MaxKey, MinKey]"
                      ],
                      "age" : [
                              "[MaxKey, MinKey]"
    ......
 "ok" : 1
}

通过上面的不同场景,得出如下:
排序使用到索引的情形
        db.persons.find().sort({name:1,age:1})
        db.persons.find().sort({name:-1,age:-1})

排序未使用到索引的情形
        db.persons.find().sort({name:1,age:-1})
        db.persons.find().sort({name:-1,age:1})
        db.persons.find().sort({age:1,name:1})
        db.persons.find().sort({age:-1,name:1})

4、复合索引与索引前缀

    索引前缀指的是复合索引的子集
    假如存在如下索引

    { "item": 1, "location": 1, "stock": 1 }

    那存在下列索引前缀
    { item: 1 }
    { item: 1, location: 1 }

    在MongoDB中,下列查询过滤条件情形中,索引将会被使用到
            item字段
            item字段 + location字段
            item字段 + location字段 + stock字段
            item字段 + location字段(尽管索引被使用,但不高效)

    以下过滤条件查询情形,索引将不会被使用到
            location字段
        stock字段
        location + stock字段

5、小结
a、复合索引是基于多个键(列)上创建的索引
b、复合索引在创建的时候可以为其每个键(列)来指定排序方法
c、索引键列的排序方法影响查询在排序时候的操作,方向一致或相反的才能被匹配
d、复合索引与前缀索引通常在匹配的情形下才能被使用

时间: 2024-08-04 04:40:31

MongoDB 复合索引的相关文章

MongoDB 唯一索引

MongoDB支持的索引种类很多,诸如单键索引,复合索引,多键索引,TTL索引,文本索引,空间地理索引等.同时索引的属性可以具有唯一性,即唯一索引.唯一索引用于确保索引字段不存储重复的值,即强制索引字段的唯一性.缺省情况下,MongoDB的_id字段在创建集合的时候会自动创建一个唯一索引.本文主要描述唯一索引的用法. 一.创建唯一索引语法 //语法 db.collection.createIndex( <key and index type specification>, { unique:

MongoDB 部分索引(Partial Indexes)

MongoDB部分索引只为那些在一个集合中,满足指定的筛选条件的文档创建索引.由于部分索引是一个集合文档的一个子集,因此部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本.部分索引通过指定过滤条件来创建,可以为MongoDB支持的所有索引类型使用部分索引. 一.语法描述 创建部分索引语法 db.collection.createIndex(keys, options) options可以使用partialFilterExpression,即部分过滤表达式,其类型为文档类型 过滤表达式通

数据-mongodb创建索引后过段时间就消失了?

问题描述 mongodb创建索引后过段时间就消失了? 对A,B两个集合同时创建两组复合索引,索引字段都才4个,创建成功后查询速度比原来快多了.过一天后查询看发现A集合又慢了,发现A集合的复合索引没有了,B集合的索引还在.(A集合数据比较大一大) 给A重新创建了几次索引,都是过段时间索引就自动消失了.要重新创建.求解啊 解决方案 这个应该不会主动消失 是不是倒入数据的时候又覆盖索引 解决方案二: 这个不应该把,索引创建了,只有有人删除了,才会没有. 不太可能说没人删除,就自动没有了,那mongod

MongoDB TTL索引

TTL索引是一种特殊类型的单字段索引,主要用于当满足某个特定时间之后自动删除相应的文档.也就是说集合中的文档有一定的有效期,超过有效期的文档就会失效,会被移除.也即是数据会过期.过期的数据无需保留,这种情形适用于如机器生成的事件数据,日志和会话信息等等.本文主要描述TTL索引的使用. 一.TTL索引 创建方法 db.collection.createIndex(keys, options) options: expireAfterSeconds 指定多少秒或者包含日期值的数组 创建示例 db.e

pymongo给mongodb创建索引的简单实现方法

  这篇文章主要介绍了pymongo给mongodb创建索引的简单实现方法,涉及Python使用pymongo模块操作mongodb的技巧,需要的朋友可以参考下 下面的代码给user的user_name字段创建唯一索引 ? 1 2 3 4 import pymongo mongo = pymongo.Connection('localhost') collection = mongo['database']['user'] collection.ensure_index('user_name',

使用索引的误区之一:没有使用复合索引的前导列导致查询不使用索引

索引 使用索引的误区之一:没有使用复合索引的前导列导致查询不使用索引在oracle中,我们经常以为建立了索引,sql查询的时候就会如我们所希望的那样使用索引,事实上,oracle只会在一定条件下使用索引,这里我们总结数第一点:oracle会在条件中包含了前导列时使用索引,即查询条件中必须使用索引中的第一个列,请看下面的例子 SQL> select * from tab;   TNAME                          TABTYPE  CLUSTERID -----------

Oracle中含常数的复合索引

原来对于索引的认识只知道索引可以基于一个或者多个列,B-Tree索引不包含null,但有些情况下我们又需要通过where 列名 is null来查找一些数据,这时候数据库由于没办法使用索引就会使用全表扫描,导致执行效率低下,这时候我们可以通过使用含常数的复合索引来解决这个问题. 下面开始进行实验: 首先建立测试表 SYS@ORCL>create table test_objects nologging as select rownum id,a.* from dba_objects a wher

Oracle中常数复合索引的应用案例

从一个客户的真实优化案例引申的问题. 客户的一个数据库需要进行优化,不过由于程序开发方没有介入,因此这次优化无法对SQL进行修改. 仅对数据库级的调整一般来说收效不大,不过发现客户数据库中个别的SQL存在性能问题,且这个性能问题已经影响到整个数据库.如果可以将这个SQL优化,那么可以解决目前数据库的性能问题.幸运的是,这个问题可以通过添加索引来进行优化. 模拟问题SQL如下: SQL> select * from v$version; BANNER -----------------------

空间复合索引加速空间搜索

标签 PostgreSQL , PostGIS , 复合索引 , btree_gist , 共享单车 背景 随着移动互联网的普及,空间数据已经成为大多数企业数据的标配,例如出行.快递.等. 通常数据的查询会带位置距离搜索,同时还会伴随其他属性的过滤,其他属性的过滤:例如时间范围,区域ID的过滤,物流公司ID的过滤. 空间索引和BTREE索引在PostgreSQL中属于两种索引(PostgreSQL支持btree,hash,gin,gist,sp-gist,brin,rum,bloom,zoomd