MongoDB进阶篇-索引(索引概述、索引的类型、索引相关操作、索引的使用)

文章目录

  • [1. 索引概述](#1. 索引概述)
  • [2. 索引的类型](#2. 索引的类型)
    • [2.1 单字段索引](#2.1 单字段索引)
    • [2.2 复合索引](#2.2 复合索引)
    • [2.3 其他索引](#2.3 其他索引)
      • [2.3.1 地理空间索引(Geospatial Index)](#2.3.1 地理空间索引(Geospatial Index))
      • [2.3.2 文本索引(Text Indexes)](#2.3.2 文本索引(Text Indexes))
      • [2.3.3 哈希索引(Hashed Indexes)](#2.3.3 哈希索引(Hashed Indexes))
  • [3. 索引相关操作](#3. 索引相关操作)
    • [3.1 查看索引](#3.1 查看索引)
    • [3.2 创建索引](#3.2 创建索引)
      • [3.3.1 创建单字段索引](#3.3.1 创建单字段索引)
      • [3.3.2 创建复合索引](#3.3.2 创建复合索引)
      • [3.3.3 创建文本索引](#3.3.3 创建文本索引)
    • [3.4 移除索引](#3.4 移除索引)
      • [3.4.1 移除指定索引](#3.4.1 移除指定索引)
      • [3.4.2 移除所有索引](#3.4.2 移除所有索引)
  • [4. 索引的使用](#4. 索引的使用)
    • [4.1 执行计划](#4.1 执行计划)
    • [4.2 执行计划中各个字段的含义](#4.2 执行计划中各个字段的含义)
    • [4.3 stage字段的取值及含义](#4.3 stage字段的取值及含义)
    • [4.4 覆盖查询](#4.4 覆盖查询)

阅读本文前可以先阅读以下文章:

1. 索引概述

MongoDB 索引的官网文档:索引-MongoDB手册


索引支持在 MongoDB 中高效执行查询。如果没有索引,MongoDB 就必须扫描集合中的每个文档以返回查询结果。如果查询存在适当的索引,MongoDB 就可以使用该索引来限制其必须扫描的文档数

索引可提高查询性能,但添加索引会影响写入操作的性能。对于写入读取率高的集合,由于每次插入操作都必须同时更新所有索引,因此会带来较高的索引成本

MongoDB 索引使用 B-Tree 数据结构(MySQL 是 B+Tree)

2. 索引的类型

2.1 单字段索引

MongoDB 支持在文档的单个字段上创建用户定义的升序索引或降序索引,称为单字段索引(Single Field Index)

对于单个字段索引和排序操作,索引键的排序顺序(即升序或降序)并不重要,因为 MongoDB 可以在任何方向上遍历索引

2.2 复合索引

MongoDB 支持多个字段的自定义索引,即复合索引(Compound Index)

复合索引中列出的字段顺序具有重要意义。例如,如果复合索引由 { user_id: 1, score: -1 } 组成,则索引首先会按 user_id 正序排序,然后在每个 user_id 的值内,再按 score 倒序排序

2.3 其他索引

2.3.1 地理空间索引(Geospatial Index)

为了支持对地理空间坐标数据的有效查询,MongoDB 提供了两种特殊的索引:返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引

2.3.2 文本索引(Text Indexes)

文本索引的特点:

  • 分词:MongoDB 在创建文本索引时会对字段内容进行分词处理,将文本分解成单词或术语(tokens)
  • 权重:可以为不同的字段指定不同的权重,以便在搜索时影响文档的相关性得分
  • 停用词:MongoDB 会忽略某些常用词(如 "the"、"and" 等),这些词被称为停用词。MongoDB 有一个内置的停用词列表,也可以自定义停用词列表
  • 语言支持:MongoDB 的文本索引支持多种语言的分词和搜索

注意事项:

  • 文本索引不存储停止词和词干。这意味着它们不会影响索引的大小
  • 文本索引不能用于文本字段中的二进制数据
  • 文本索引不能用于数组字段中的字符串元素
  • $text 查询不能与 $$$ 运算符一起使用

2.3.3 哈希索引(Hashed Indexes)

为了支持基于散列的分片,MongoDB 提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支持相等匹配,不支持基于范围的查询

3. 索引相关操作

3.1 查看索引

查看索引的语法

sql 复制代码
db.collection.getIndexes()

查看 comment 集合中所有的索引

sql 复制代码
db.collection.getIndexes()

查询结果如下

json 复制代码
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]

结果中显示的是默认的 _id_ 索引(MongoDB 在创建集合的过程中,会在 _id 字段上创建一个唯一的索引,默认名字为 _id_,该索引可防止插入两个具有相同 _id 值的文档)

  • _id 索引是唯一索引,因此 _id 值不能重复
  • 在分片集群中,通常使用 _id 作为片键

3.2 创建索引

创建索引的语法

sql 复制代码
db.collection.createIndex(keys, options)
参数名 类型 描述 必需
keys 文档 指定索引的字段和索引类型。对于文本索引,字段类型应该设置为 "text"。可以指定单个字段或多个字段
options 文档 索引的额外选项。这是一个可选参数,可以包含多个字段

options 参数的详细说明:

选项 类型 描述 默认值
background 布尔 是否在后台创建索引。如果为 true,MongoDB 将在后台创建索引,这样就不会阻塞其他数据库操作 false
unique 布尔 是否创建唯一索引。如果为 true,则索引字段的值必须是唯一的 false
name 字符串 索引的名称。如果未指定,MongoDB 会自动生成一个名称 自动生成
partialFilterExpression 文档 指定部分索引的过滤条件。只有满足条件的文档才会包含在索引中
sparse 布尔 是否创建稀疏索引。如果为 true,则索引只包含具有指定字段的文档,忽略没有该字段的文档 false
expireAfterSeconds 整数 对于具有日期类型的字段,指定文档在集合中的生存时间(TTL)
storageEngine 文档 指定索引的存储引擎配置 使用默认存储引擎配置
weights 文档 对于文本索引,为索引中的每个字段指定权重。权重越高,字段在文本搜索中的相关性得分越高 所有字段权重相等

3.3.1 创建单字段索引

对 user_id 字段建立单字段索引

sql 复制代码
db.comment.createIndex({ user_id: 1 });

再次查看 comment 集合中所有的索引

json 复制代码
[
  { v: 2, key: { _id: 1 }, name: '_id_' },
  { v: 2, key: { user_id: 1 }, name: 'user_id_1' }
]

3.3.2 创建复合索引

对 user_id 和 nickname 建立复合(Compound)索引

sql 复制代码
db.comment.createIndex({ userid: 1, nickname: -1 });

再次查看 comment 集合中所有的索引

json 复制代码
[
  { v: 2, key: { _id: 1 }, name: '_id_' },
  { v: 2, key: { user_id: 1 }, name: 'user_id_1' },
  {
    v: 2,
    key: { userid: 1, nickname: -1 },
    name: 'userid_1_nickname_-1'
  }
]

3.3.3 创建文本索引

对 content 字段建立单字段索引

sql 复制代码
db.comment.createIndex({ content: "text" });

3.4 移除索引

3.4.1 移除指定索引

sql 复制代码
db.collection.dropIndex(index);

删除 comment 集合中 user_id 字段上的升序索引

sql 复制代码
db.comment.dropIndex(
    { 
        user_id: 1 
    }
);

3.4.2 移除所有索引

sql 复制代码
db.collection.dropIndexes()

删除 comment 集合中的所有索引

sql 复制代码
db.comment.dropIndexes()

_id 的字段的索引是无法删除的,只能删除非 _id 字段的索引

4. 索引的使用

4.1 执行计划

分析查询性能(Analyze Query Performance)通常使用执行计划(Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等

sql 复制代码
db.collection.find(query,options).explain(options)
参数名 类型 描述 必需
query document 查询选择器,用于指定查询条件
options document 可选的。用于修改查询的默认行为的各种选项,如排序、限制等
explain function 用于获取查询执行计划的详细信息 是(调用时)
explainOptions document 可选的。用于修改解释操作的默认行为的各种选项

每个参数的详细解释:

  • query: 这是一个文档,用于定义查询条件。它可以是简单的字段等值查询,也可以是复杂的条件组合,包括逻辑运算符和正则表达式
  • options: 这是一个文档,可以包含多个键,用于控制查询的行为。常见的选项包括:
    • sort: 排序条件
    • limit: 限制返回的文档数量
    • skip: 跳过文档的数量。
    • projection: 投影,用于指定返回的字段
  • explain: 这是一个函数,当你在查询后面调用它时,MongoDB 不会返回查询结果,而是返回查询执行计划的详细信息。这有助于理解查询是如何执行的,以及如何优化查询
  • explainOptions: 这是一个文档,用于控制解释操作的输出。例如,可以指定是否返回所有阶段的执行计划或者只返回获胜计划

根据 user_id 字段查询数据

sql 复制代码
db.comment.find({ user_id: "1003" }).explain();

MongoDB 返回的结果

json 复制代码
{
  explainVersion: '1',
  queryPlanner: {
    namespace: 'test.comment',
    parsedQuery: {
      user_id: {
        '$eq': '1003'
      }
    },
    indexFilterSet: false,
    queryHash: 'B7F3AE51',
    planCacheKey: '8C1EE785',
    optimizationTimeMillis: 0,
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    prunedSimilarIndexes: false,
    winningPlan: {
      isCached: false,
      stage: 'COLLSCAN',
      filter: {
        user_id: {
          '$eq': '1003'
        }
      },
      direction: 'forward'
    },
    rejectedPlans: []
  },
  command: {
    find: 'comment',
    filter: {
      user_id: '1003'
    },
    '$db': 'test'
  },
  serverInfo: {
    host: 'LAPTOP-G7HILK54',
    port: 27017,
    version: '8.0.3',
    gitVersion: '89d97f2744a2b9851ddfb51bdf22f687562d9b06'
  },
  serverParameters: {
    internalQueryFacetBufferSizeBytes: 104857600,
    internalQueryFacetMaxOutputDocSizeBytes: 104857600,
    internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
    internalDocumentSourceGroupMaxMemoryBytes: 104857600,
    internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
    internalQueryProhibitBlockingMergeOnMongoS: 0,
    internalQueryMaxAddToSetBytes: 104857600,
    internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
    internalQueryFrameworkControl: 'trySbeRestricted',
    internalQueryPlannerIgnoreIndexWithCollationForRegex: 1
  },
  ok: 1
}

重点关注 winningPlan 中的 stage 字段(COLLSCAN 表示全盘扫描)

下面对 user_id 字段建立索引

sql 复制代码
db.comment.createIndex({ user_id: 1 });

再次查看执行计划

json 复制代码
{
  explainVersion: '1',
  queryPlanner: {
    namespace: 'test.comment',
    parsedQuery: {
      user_id: {
        '$eq': '1003'
      }
    },
    indexFilterSet: false,
    queryHash: 'B7F3AE51',
    planCacheKey: '57E4C731',
    optimizationTimeMillis: 0,
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    prunedSimilarIndexes: false,
    winningPlan: {
      isCached: false,
      stage: 'FETCH',
      inputStage: {
        stage: 'IXSCAN',
        keyPattern: {
          user_id: 1
        },
        indexName: 'user_id_1',
        isMultiKey: false,
        multiKeyPaths: {
          user_id: []
        },
        isUnique: false,
        isSparse: false,
        isPartial: false,
        indexVersion: 2,
        direction: 'forward',
        indexBounds: {
          user_id: [
            '["1003", "1003"]'
          ]
        }
      }
    },
    rejectedPlans: []
  },
  command: {
    find: 'comment',
    filter: {
      user_id: '1003'
    },
    '$db': 'test'
  },
  serverInfo: {
    host: 'LAPTOP-G7HILK54',
    port: 27017,
    version: '8.0.3',
    gitVersion: '89d97f2744a2b9851ddfb51bdf22f687562d9b06'
  },
  serverParameters: {
    internalQueryFacetBufferSizeBytes: 104857600,
    internalQueryFacetMaxOutputDocSizeBytes: 104857600,
    internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
    internalDocumentSourceGroupMaxMemoryBytes: 104857600,
    internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
    internalQueryProhibitBlockingMergeOnMongoS: 0,
    internalQueryMaxAddToSetBytes: 104857600,
    internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
    internalQueryFrameworkControl: 'trySbeRestricted',
    internalQueryPlannerIgnoreIndexWithCollationForRegex: 1
  },
  ok: 1
}

可以发现,stage 字段已经变成了 FETCH,inputStage 属性里面的 stage 字段变成了 IXSCAN(基于索引的扫描)

4.2 执行计划中各个字段的含义

字段名 描述
explainVersion 解释输出的版本号。
queryPlanner 查询计划器的详细信息。
namespace 执行查询的命名空间(数据库和集合)。
parsedQuery 解析后的查询条件。
indexFilterSet 是否设置了索引过滤器。
queryHash 查询的哈希值。
planCacheKey 用于查询缓存的键。
optimizationTimeMillis 查询优化所花费的时间(毫秒)。
maxIndexedOrSolutionsReached 是否达到了索引 OR 解决方案的最大数量。
maxIndexedAndSolutionsReached 是否达到了索引 AND 解决方案的最大数量。
maxScansToExplodeReached 是否达到了索引爆炸扫描的最大数量。
prunedSimilarIndexes 是否修剪了相似的索引。
winningPlan 被选中的查询计划。
isCached 是否从计划缓存中检索到计划。
stage 查询执行的阶段。
inputStage 当前阶段的输入阶段(用于嵌套阶段)。
keyPattern 索引的键模式。
indexName 索引的名称。
isMultiKey 索引是否是多键索引。
multiKeyPaths 包含多键路径的索引字段。
isUnique 索引是否是唯一索引。
isSparse 索引是否是稀疏索引。
isPartial 索引是否是部分索引。
indexVersion 索引的版本。
direction 索引扫描的方向。
indexBounds 索引扫描的边界。
rejectedPlans 被拒绝的查询计划列表。
command 执行的命令的详细信息。
serverInfo 服务器信息,包括主机名、端口、版本等。
serverParameters 影响查询执行的服务器参数。
ok 命令是否成功执行的标志。

4.3 stage字段的取值及含义

阶段名称 描述
COLLSCAN 集合扫描,即全集合扫描,没有使用索引。
IXSCAN 索引扫描,使用索引来查找文档。
FETCH 获取阶段,用于检索索引扫描后找到的文档的其余字段。
SHARD_MERGE 在分片集群中,合并来自不同分片的查询结果。
SORT 排序阶段,对结果进行排序。
LIMIT 限制阶段,限制返回的文档数量。
SKIP 跳过阶段,跳过指定数量的文档。
IDHACK 对于 _id 的查询,MongoDB 可以使用特殊的优化。
SHARDING_FILTER 在分片集群中,用于过滤掉不属于当前查询的分片数据的阶段。
PROJECTION 投影阶段,只返回文档中的特定字段。
TEXT 文本搜索阶段,用于文本索引的搜索。
GEONEAR 地理空间查询阶段,用于查找最接近某个点的文档。
GEOFILTER 地理空间过滤阶段,用于过滤地理空间查询的结果。
COUNT 计数阶段,用于 count 操作。
COUNT_SCAN 使用索引进行计数扫描的阶段。
COUNT_SCAN_WITH_FILTER 使用索引进行计数扫描,并且应用过滤器的阶段。
DISTINCT_SCAN 用于 distinct 操作的索引扫描阶段。
SUBPLAN 子计划阶段,用于处理复杂查询的一部分。
IXHASH 使用散列索引的阶段。
FORCED_SCAN 强制进行集合扫描,即使存在索引。
COVERED 索引覆盖查询,所有需要的字段都在索引中,不需要回表查询。
EOF 查询结束。

4.4 覆盖查询

当查询条件和查询的投影仅包含索引字段时,MongoDB 会直接从索引返回结果,而不扫描任何文档或将文档带入内存,这些覆盖的查询非常高效(类似于 MySQL 中的覆盖索引)


sql 复制代码
db.comment.find(
  { user_id: "1003" },
  { user_id: 1, _id: 0 }
).explain();

MongoDB 返回的结果

json 复制代码
{
  explainVersion: '1',
  queryPlanner: {
    namespace: 'test.comment',
    parsedQuery: {
      user_id: {
        '$eq': '1003'
      }
    },
    indexFilterSet: false,
    queryHash: 'DC80EEEF',
    planCacheKey: 'B8237218',
    optimizationTimeMillis: 0,
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    prunedSimilarIndexes: false,
    winningPlan: {
      isCached: false,
      stage: 'PROJECTION_COVERED',
      transformBy: {
        user_id: 1,
        _id: 0
      },
      inputStage: {
        stage: 'IXSCAN',
        keyPattern: {
          user_id: 1
        },
        indexName: 'user_id_1',
        isMultiKey: false,
        multiKeyPaths: {
          user_id: []
        },
        isUnique: false,
        isSparse: false,
        isPartial: false,
        indexVersion: 2,
        direction: 'forward',
        indexBounds: {
          user_id: [
            '["1003", "1003"]'
          ]
        }
      }
    },
    rejectedPlans: []
  },
  command: {
    find: 'comment',
    filter: {
      user_id: '1003'
    },
    projection: {
      user_id: 1,
      _id: 0
    },
    '$db': 'test'
  },
  serverInfo: {
    host: 'LAPTOP-G7HILK54',
    port: 27017,
    version: '8.0.3',
    gitVersion: '89d97f2744a2b9851ddfb51bdf22f687562d9b06'
  },
  serverParameters: {
    internalQueryFacetBufferSizeBytes: 104857600,
    internalQueryFacetMaxOutputDocSizeBytes: 104857600,
    internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
    internalDocumentSourceGroupMaxMemoryBytes: 104857600,
    internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
    internalQueryProhibitBlockingMergeOnMongoS: 0,
    internalQueryMaxAddToSetBytes: 104857600,
    internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
    internalQueryFrameworkControl: 'trySbeRestricted',
    internalQueryPlannerIgnoreIndexWithCollationForRegex: 1
  },
  ok: 1
}
相关推荐
了一li39 分钟前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
码农君莫笑1 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
别致的影分身1 小时前
使用C语言连接MySQL
数据库·mysql
京东零售技术3 小时前
“慢”增长时代的企业数据体系建设:超越数据中台
数据库
sdaxue.com3 小时前
帝国CMS:如何去掉帝国CMS登录界面的认证码登录
数据库·github·网站·帝国cms·认证码
o(╥﹏╥)4 小时前
linux(ubuntu )卡死怎么强制重启
linux·数据库·ubuntu·系统安全
阿里嘎多学长4 小时前
docker怎么部署高斯数据库
运维·数据库·docker·容器
Yuan_o_4 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Sunyanhui14 小时前
牛客网 SQL36查找后排序
数据库·sql·mysql
老王笔记5 小时前
MHA binlog server
数据库·mysql