mongodb索引

创建索引

单键或复合键

创建索引命令:db.<collection>.createIndex(<keys>, <options>)
返回结果:

{
      createCollectionAutomatically: false,
      numIndexesBefore: 1,
      numIndexesAfter: 2,
      ok: 1
}
  • createCollectionAutomatically是否自动创建了集合(集合不存在时,直接创建索引会导致该属性为true。集合已存在,创建索引,该值为false)
  • numIndexesBefore创建索引之前,索引的数量
  • numIndexesAfter创建索引之后,索引的数量
  • ok是否成功

复合键索引的顺序很重要,其可支持的查询方式为前缀子查询
如(A,B,C)索引支持的查询方式为(A)、(A,B)、(B,A)、(A,B,C)、(B,A,C)、(B,C,A)、(C,A,B)、(C,B,A)。例子如下:

上述索引已经按照{name: 1, currency: 1, balance: -1}的方式创建索引了,可以看到查询携带name属性的查询方式时,创建的索引才会被用到。而不携带name属性时,创建的索引无作用,还是需要通过遍历进行查找。

多键索引

创建索引的key是数组形式时,MongoDB会自动创建多键索引。此时数组中的每一个元素都会作为索引中的一个键,被保存起来,分别指向对应的完整文档的内存地址。

例如如下集合:

[
      {name: 'alice', currency: ['CNY', 'USD']}
      {name: 'bob', currency: ['GBP', 'USD']}
]

创建索引:db.<collection>.createIndex({currency: 1}),此时的索引映射情况如下:

'CNY' ---> {name: 'alice', currency: ['CNY', 'USD']}
'GBP' ---> {name: 'bob', currency: ['GBP', 'USD']}
'USD' ---> {name: 'alice', currency: ['CNY', 'USD']}
'USD' ---> {name: 'bob', currency: ['GBP', 'USD']}

如果查询操作是针对数据中元素而执行的,此时就会用到多键索引

查看索引

命令:db.<collection>.getIndexes(),返回数组形式的所有索引情况,如:

[
        {
                "v" : 2,
                "key" : {      //创建索引时的key
                        "_id" : 1
                },
                "name" : "_id_"      //索引名称
        },
        {
                "v" : 2,
                "key" : {
                        "name" : 1
                },
                "name" : "name_1"
        }
]

索引效果

查询效果

使用explain()可查看当前查询效果,返回值中注意一下信息:

{
      queryPlanner: {
            winningPlan: {
                  stage: 'COLLSCAN',    //COLLSCAN表示数据库没找到很好的查询方法,直接全集合扫描。如果是这样,说明没有用到索引
                  ......
            }
      }
      ......
}
  • queryPlanner 显示的是被查询优化器选择出来的查询计划
  • winningPlan 文档类型,详细显示查询优化程序选择的查询计划
  • winningPlan.stage 阶段名称。每个阶段都有每个阶段特有的信息。例如,IXSCAN 阶段将包括索引边界以及特定于索引扫描的其他数据。如果阶段具有子阶段或多个子阶段,则阶段将具有inputStageinputStages

阶段操作描述

描述 说明
COLLSCAN 集合扫描
IXSCAN 索引扫描
FETCH 检出文档
SHARD_MERGE 合并分片中结果
SHARDING_FILTER 分片中过滤掉孤立文档
LIMIT 使用limit 限制返回数
PROJECTION 使用 skip 进行跳过
IDHACK 针对_id进行查询
COUNT 利用db.coll.explain().count()之类进行count运算
COUNTSCAN count不使用Index进行count时的stage返回
COUNT_SCAN count使用了Index进行count时的stage返回
SUBPLA 未使用到索引的$or查询的stage返回
TEXT 使用全文索引进行查询时候的stage返回
PROJECTION 限定返回字段时候stage的返回

如果是用到了索引,则上面的stage不会为COLLSCAN。举个利用到索引的explain返回例子:

{
      queryPlanner: {
            winningPlan : {
                  "stage" : "FETCH",
                  "inputStage" : {
                        "stage" : "IXSCAN",            //index scan索引
                        "keyPattern" : {                  //通过哪个索引
                              "name" : 1
                        },
                        "indexName" : "name_1",            //使用的索引的名称
                        ......
                  }
                  ......
            }
      }
      ......
}

上面的信息,表明了两个阶段,一是IXSCAN通过索引进行扫描,一是FETCH检出文档。

检出效果

上面的explain操作中,存在FETCH阶段。此时更好的优化策略是将FETCH也消除掉,使用方法是将检出的文档返回字段被索引覆盖到,就会更加高效

此处要注意一个地方,返回的字段为复合索引字段的任意非空子集(无需前缀无需顺序),都可以让查询优化器使用到索引,从而不需要从真正的文档中检出字段,直接从索引中检出

排序效果

如果需要排序也要能利用到复合索引,条件比较苛刻,需要满足以下几点:

  • 前缀子查询
  • 顺序与索引顺序一致
  • 排序方式与索引一致(字段指定的1或-1)
  • 只首个属性的排序时,1或-1都可

如建立了复合索引{a: 1, b:-1},当sort({a:1})sort({a:-1})sort({a: 1, b:-1})时,可用到IXSCAN。而使用sort({a:1, b:1})sort({a:-1, b:-1})sort({b:-1, a:1})sort({b:-1})时,则会出现COLLSCAN

删除索引

命令db.<collection>.dropIndex(<索引名称或索引定义>)

如果需要修改已经创建的索引,需要先删除,然后再新建

唯一索引

命令:db.<collection>.createIndex(<key>, {unique: true})

其限制key中的索引值在所有文档中只能存在唯一的值,不能重复。如果文档中,已经存在该属性重复的一些文档,该创建索引的命令将不会执行成功。

还要注意一个小细节:
当唯一索引创建成功后,如果新加入的文档不包含该索引所定义的字段时,由于唯一性约束,新文档中该字段会被认为null(不会被查询出来),而如果继续插入的文档还是不包含该字段,则不能进行插入了,因为null已经存在了。

所以,如果新增的文档中不包含唯一性索引字段,则只有第一篇缺失该字段的文档将会被写入数据库

复合键索引情况类似,存在几种情况:

  • 完全不包含复合索引的字段时,新插入的第一篇的文档的所有复合索引字段的值,会被认为是null。再次插入相同的文档,则不会被允许
  • 包含部分复合索引的字段时,其他索引字段会被认为是null,再次插入时也不会被允许

总结特点,没有值的字段都认为是null,组合起来的对象不能重复

例如,创建唯一复合索引{name: 1, balance: -1},则以下文档是可以同时进行存储的:

[
      {name: 'a'},
      {balance: 100},
      {name: 'a', balance: 100}
      {num: 1}
]

而再次插入上面任意一条,都不会被允许。

稀疏性

建立稀疏性索引的命令:db.<collection>.createIndex(<key>, {sparse: true})
稀疏性相对于唯一性而言,文档限制就较为宽泛了。如果建立了稀疏性的索引,则只会将包含索引字段的文档加入索引中(即使该字段为null)

例如,建立了索引:db.<collection>.ceateIndex({name: 1}, {sparse: true}),就会存在以下文档的情况:

{name: 'a', balance: 100}      //会被加入索引
{balance: 200}      //不会被加入索引,但会被存储
{name: null, balance: 50}      //会被加入索引

利用稀疏性索引,可以节省索引所占用的存储空间。当集合中有很大一部分的文档是缺少该字段时,就没有必要将这些文档加入索引中了。
复合索引的稀疏性可以任意组合的重复插入。

唯一性与稀疏性可以同时设置
索引既可以是唯一的,也可以是稀疏的。利用两者的组合,可以解决唯一性索引只能保存第一篇缺失索引的文档,使得可以保存多篇缺失索引的文档。总结特点就是,存在该字段的,保证字段值唯一,不存在该字段的,可以存入,但不加入索引。这样就提升了集合的灵活性。

生存时间

命令:db.<collection>.createIndex(<key>, {expireAfterSeconds: <秒数>})

注意,此时key中的键,只能是时间属性或包含时间属性的数组,如果是其他类型的字段将不会触发过期删除的特性。另外,时间过期设置对复合键索引无效

字段为包含时间属性的数组时,以最小(最久远)的那个日期为准,用以计算是否过期。

数据库使用后台线程用来删除过期文档,是具有一定的延迟的,该延迟具有不确定性。

posted @ 2020-10-17 12:25  Mr_Kahn  阅读(70)  评论(0)    收藏  举报