文档菜单
文档首页
/
MongoDB 手册
/

在已填充集合上构建索引

本页内容

  • 行为
  • 索引构建对数据库性能的影响
  • 在复制环境中构建索引
  • 构建失败和恢复
  • 监视正在进行的索引构建
  • 终止正在进行的索引构建
  • 索引构建过程

索引构建使用优化的构建过程,在索引构建的开始和结束时对集合持有独占锁。其余构建过程则允许交错读取和写入操作。有关索引构建过程和锁定行为的详细说明,请参阅索引构建过程.

在副本集或分片集群上构建索引将在所有承载数据的数据副本集成员上同时进行。主节点需要完成构建前必须完成的最小数据承载投票成员数(即提交法定人数),包括自身。任何投票成员是指任何副本集成员,其中members[n].votes 大于 0。有关更多信息,请参阅副本环境中的索引构建

从 MongoDB 7.1 开始,索引构建得到改进,具有更快的错误报告和更高的故障恢复能力。您还可以使用新的 indexBuildMinAvailableDiskSpaceMB 参数设置索引构建所需的最低可用磁盘空间,如果磁盘空间过低,则停止索引构建。

以下表格比较了从 MongoDB 7.1 开始的索引构建行为与早期版本的行为。

从 MongoDB 7.1 开始的行为
早期 MongoDB 版本中的行为
在集合扫描阶段发现的索引错误(除了重复键错误外)将立即返回,然后停止索引构建。早期 MongoDB 版本在提交阶段返回错误,该阶段发生在索引构建的接近结束时。MongoDB 7.1 帮助您快速诊断索引错误。例如,如果找到不兼容的索引值格式,错误将立即返回给您。
与 MongoDB 7.1 相比,索引构建错误可能需要较长时间才能返回,因为错误是在提交阶段、索引构建接近结束时返回的。
提高了部署的弹性。如果发生索引构建错误,一个 次要 成员可以请求 主要 成员停止索引构建,而次要成员不会崩溃。停止索引构建的请求并不总是可能的:如果成员已经投票提交索引,则次要成员无法请求停止索引构建,并且次要成员会崩溃(类似于 MongoDB 7.0 和更早版本)。
索引构建错误可能导致次要成员崩溃。
改进了索引构建的磁盘空间管理。如果可用磁盘空间低于 indexBuildMinAvailableDiskSpaceMB 参数中指定的最低值,则可能自动停止索引构建。如果成员已经投票提交索引,则不会停止索引构建。
如果可用磁盘空间不足,则不会停止索引构建。

注意

有关在 Atlas 中创建索引的信息,请参阅 Atlas 文档中的 索引管理 页面。

MongoDB的早期版本支持在前台或后台构建索引。前台索引构建速度快,产生的索引数据结构更高效,但需要阻塞被索引集合的父数据库的读写访问,直到构建完成。后台索引构建较慢,结果效率较低,但在构建过程中允许对数据库及其集合进行读写访问。

索引构建现在只在构建的开始和结束时对被索引的集合获取独占锁,以保护元数据更改。构建过程的其余部分使用后台索引构建的让步行为,以最大化构建过程中的读写访问。

优化的索引构建性能至少与后台索引构建相当。对于在构建过程中几乎或没有接收到的更新负载,优化索引构建可以与同一数据的前台索引构建一样快。

使用 db.currentOp() 监控正在进行的索引构建进度。

MongoDB如果指定给 createIndexes 或其shell辅助工具 createIndex()createIndexes(),将忽略“后台”索引构建选项。

对于对集合执行约束的索引,例如唯一索引,在索引构建完成后,mongod会检查所有预存在和并发写入的文档是否违反这些约束。在索引构建过程中可能会存在违反索引约束的文档。如果在构建结束时任何文档违反索引约束,则mongod将终止构建并抛出错误。

例如,考虑一个已填充的集合inventory。管理员想要在product_sku字段上创建一个唯一索引。如果集合中的任何文档在product_sku字段上有重复值,索引构建仍然可以成功启动。如果构建结束时仍存在任何违反,则mongod将终止构建并抛出错误。

同样,在索引构建进行时,应用程序可以成功向inventory集合写入具有重复product_sku值的文档。如果构建结束时仍存在任何违反,则mongod将终止构建并抛出错误。

为了降低因约束违反而导致索引构建失败的风险

  • 验证集合中没有任何文档违反索引约束。

  • 停止所有无法保证无违反写入操作的应用程序对集合的写入。

对于分布在多个分片上的分片集合,一个或多个分片可能包含具有重复文档的数据块。因此,创建索引操作可能在某些分片(即没有重复的)上成功,但在其他分片(即有重复的)上失败。为了避免在分片之间留下不一致的索引,您可以从mongos发出db.collection.dropIndex()以从集合中删除索引。

为了降低这种情况的风险,在创建索引之前

  • 验证集合中没有任何文档违反索引约束。

  • 停止所有无法保证无违反写入操作的应用程序对集合的写入。

提示

另请参阅

默认情况下,服务器允许最多同时构建三个索引。要更改允许的并发索引构建数量,请修改 maxNumActiveUserIndexBuilds 参数。

如果并发索引构建的数量达到 maxNumActiveUserIndexBuilds 指定的限制,服务器将阻止进一步的索引构建,直到并发索引构建的数量低于限制。

在目标集合处于高写负载的时间段内建立索引可能会导致写性能降低和索引建立时间更长。

考虑指定一个维护窗口,在此期间应用程序停止或减少对集合的写操作。在此维护窗口内开始索引建立,以减轻建立过程可能产生的负面影响。

createIndexes 支持在一个集合上建立一个或多个索引。 createIndexes 使用内存和磁盘上的临时文件组合来完成索引建立。 createIndexes 默认的内存使用限制为 200 兆字节,由使用单个 createIndexes 命令建立的索引共享。一旦达到内存限制,createIndexes 将使用位于 --dbpath 目录下名为 _tmp 的子目录中的临时磁盘文件来完成建立。

您可以通过设置 maxIndexBuildMemoryUsageMegabytes 服务器参数来覆盖内存限制。设置更高的内存限制可能会导致索引建立更快完成。然而,如果与系统未使用的 RAM 相比设置得太高,可能会导致内存耗尽和服务器关闭。

如果主机机器可用的空闲 RAM 有限,您可能需要在修改 mongod RAM 使用之前安排一个维护周期来增加总系统 RAM。

注意

需要功能兼容版本4.4+

副本集或分片集群中的每个 mongod 必须将 功能兼容版本 设置为至少 4.4 才能同时在副本集成员之间启动索引构建。

在副本集或分片集群上构建的索引会在所有承载数据的副本集成员之间同时进行。对于分片集群,索引构建仅发生在包含正在索引的集合数据的分片上。主节点需要一定数量的承载数据的 投票 成员(即提交法定人数),包括自身,必须在标记索引为就绪使用之前完成构建。

重要

如果承载数据的投票节点变得不可达,并且将 提交法定人数 设置为默认的 votingMembers,则索引构建可能会挂起,直到该节点重新上线。

构建过程总结如下:

  1. 主节点接收到 createIndexes 命令并立即创建与索引构建关联的 "startIndexBuild" oplog 条目。

  2. 从节点在复制 "startIndexBuild" oplog 条目后开始索引构建。

  3. 每个成员在完成索引集合中的数据索引后“投票”提交构建。

  4. 从节点继续处理任何新的写入操作到索引,同时在等待主节点确认法定人数的投票。

  5. 当主节点拥有法定人数的投票时,它会检查任何键约束违规,例如重复键错误。

    • 如果没有键约束违规,主节点完成索引构建,标记索引为就绪使用,并创建相关的 "commitIndexBuild" oplog 条目。

    • 如果有任何键约束违规,则索引构建失败。主节点中止索引构建并创建相关的 "abortIndexBuild" oplog 条目。

  6. 从节点复制 "commitIndexBuild" oplog 条目并完成索引构建。

    如果从节点复制的是 "abortIndexBuild" oplog 条目,则它们中止索引构建并丢弃构建作业。

对于分片集群,索引构建仅发生在包含正在索引的集合数据的分片上。

有关索引构建过程的更详细描述,请参阅 索引构建过程。

默认情况下,索引构建使用提交法定人数为 "votingMembers",即所有承载数据的投票成员。要使用非默认的提交法定人数启动索引构建,请将 提交法定人数 参数指定为 createIndexes 或其 shell 辅助程序 db.collection.createIndex()db.collection.createIndexes()

要修改正在进行的同时索引构建所需的提交法定人数,请使用 setIndexCommitQuorum 命令。

注意

索引构建可能会影响副本集的性能。对于无法忍受性能下降的索引构建工作负载,考虑执行滚动索引构建过程。滚动索引构建每次最多使一个副本集成员退出,从次要成员开始,在该成员上独立构建索引。滚动索引构建至少需要一个副本集选举。

提交法定票数与写关注点之间有重要差异:

  • 索引构建使用提交法定票数。

  • 写操作使用写关注点。

集群中每个承载数据的节点都是一个投票成员。

提交法定票数指定了在主节点执行提交之前,必须有多少个承载数据的投票成员,或者哪些投票成员(包括主节点)必须准备好提交一个同时索引构建

写关注点是写操作传播到指定数量实例的确认级别。

更改版本8.0: 提交法定票数指定了在主节点提交索引构建之前,必须有多少个节点准备好完成索引构建。相比之下,当主节点提交索引构建时,写关注点指定了在命令返回成功之前,必须有多少个节点复制索引构建操作日志条目。

在以前的版本中,当主节点提交索引构建时,写关注点指定了在命令返回成功之前,必须有多少个节点完成索引构建。

从 MongoDB 5.0 开始,如果主节点 mongod 在索引构建期间进行干净的 shutdown 并带有 "force" : true 或接收到 SIGTERM 信号,并且 commitQuorum 设置为默认的 votingMembers,则索引构建进度将被保存到磁盘。当 mongod 重新启动时,它会自动恢复索引构建,并从保存的检查点继续。在早期版本中,如果索引构建被中断,则必须从头开始重新启动。

从MongoDB 5.0版本开始,如果一个辅助的 mongod 在进行索引构建过程中执行了一个干净的 shutdown 命令并带有 "force" : true 或接收了 SIGTERM 信号,并且 commitQuorum 设置为默认的 votingMembers,那么索引构建的进度将保存到磁盘。当 mongod 重新启动时,它会自动恢复索引构建并从保存的检查点继续。在早期版本中,如果索引构建被中断,必须从头开始重新启动。

mongod 可以在恢复索引构建的过程中执行启动过程。

如果您以独立模式(即删除或注释掉 replication.replSetName 或省略 --replSetName)重启 mongod,则 mongod 无法重新启动索引构建。构建将保持暂停状态,直到手动 删除

如果在索引构建过程中 mongod 关闭,则索引构建作业和所有进度都将丢失。重新启动 mongod 不会重新启动索引构建。您必须重新执行 createIndex() 操作来重新启动索引构建。

从MongoDB 5.0开始,如果在索引构建过程中节点回滚到先前状态,则将索引构建进度保存到磁盘。如果回滚完成后还有工作要做,mongod将自动恢复索引构建并从保存的检查点继续。

MongoDB可以暂停正在进行的索引构建以执行回滚。

  • 如果回滚没有撤销索引构建,MongoDB在完成回滚后重新开始索引构建。

  • 如果回滚撤销了索引构建,则必须在回滚完成后重新创建索引或索引。

如果分片集合中包含该集合数据分片的每个分片没有完全相同的索引(包括索引选项),则该分片集合的索引是不一致的。尽管在正常操作期间不应出现不一致的索引,但可能会发生,例如

  • 当用户使用具有unique键约束创建索引时,其中一个分片包含具有重复文档的数据块。在这种情况下,可能在没有重复的数据块的分片上成功执行创建索引操作,但在包含重复的数据块的分片上失败。

  • 当用户以滚动方式(即手动逐个在分片上构建索引)创建分片之间的索引,但未能为关联的分片构建索引或以不同的规范错误地构建索引。

分片集合的主配置服务器定期检查分片之间的索引一致性。要配置这些定期检查,请参阅enableShardedIndexConsistencyCheckshardedIndexConsistencyCheckIntervalMS

命令serverStatus返回字段shardedIndexConsistency,在配置服务器主节点上运行时报告索引不一致性。

要检查分片集合是否具有不一致的索引,请参阅跨分片查找不一致的索引。

要查看索引构建操作的状态,您可以使用 db.currentOp() 方法在 mongosh 中。要筛选索引创建操作的当前操作,请参阅 活动索引操作 以获取示例。

msg 字段包含索引构建过程中当前阶段的完成百分比测量。

在索引构建过程中,进度会写入到 MongoDB 日志。如果索引构建被停止并恢复,将会有包含如下字段的日志消息

"msg":"Index build: wrote resumable state to disk",
"msg":"Found index from unfinished build",

使用 dropIndexes 命令或其 shell 辅助工具 dropIndex()dropIndexes() 来终止正在进行的索引构建。有关更多信息,请参阅停止正在进行的索引构建

请不要在副本集或分片集群中使用 killOp 来终止正在进行的索引构建。

以下表格描述了索引构建过程的每个阶段

阶段
描述
锁定
mongod 对正在索引的集合获取一个排他性的 X 锁。这阻止了对该集合的所有读写操作,包括应用任何针对该集合的复制的写操作或元数据命令。mongod 不会释放此锁。
初始化

mongod 在此初始状态下创建三个数据结构

  • 初始索引元数据条目。

  • 一个临时表(“侧写表”),用于存储在构建过程中从正在索引的集合的写入操作生成的键。

  • 一个临时表(“约束违反表”),用于所有可能导致键生成错误的文档。键生成错误发生在文档具有索引字段的无效键时。例如,在构建唯一索引时字段值重复的文档,或在构建GeoJSON对象2dsphere索引

锁定
mongod 将排他性的 X 集合锁降级为意向排他性 IX 锁。mongod 会定期释放此锁,以交错读和写操作。
扫描集合

对于集合中的每个文档,mongod 为该文档生成一个键,并将该键放入外部排序器。

如果在集合扫描过程中生成键时遇到键生成错误,则将其存储在约束违反表中进行后续处理。

如果在生成键时遇到任何其他错误,则构建失败并出现错误。

一旦 mongod 完成集合扫描,它将排序后的键放入索引中。

进程侧写入表

mongod 使用先进先出优先级来清理侧写入表。

如果mongod 在处理侧写入表中的键时遇到键生成错误,它会将该键存储在约束违规表中以供后续处理。

如果mongod 在处理键时遇到任何其他错误,构建将因错误而失败。

在构建过程中,对于写入到集合中的每个文档,mongod 都会为该文档生成一个键,并将其存储在侧写入表中以供后续处理。使用快照系统来限制要处理的键的数量。

投票并等待提交法定人数

不属于复制集的mongod 跳过此阶段。

mongod 向主节点提交“投票”以提交索引。具体来说,它将“投票”写入主节点上的内部复制集合。

如果mongod 是主节点,则在继续索引构建过程之前,它将等待获得提交法定人数的投票(默认情况下为所有具有投票数据承载的成员)。

如果mongod 是辅助节点,则它将等待复制“commitIndexBuild”或“abortIndexBuild”操作记录。

  • 如果mongod 复制“commitIndexBuild”操作记录,则完成侧写入表的清理,并移动到索引构建过程的下一阶段。

  • 如果mongod 复制“abortIndexBuild”操作记录,则终止索引构建并丢弃构建作业。

在等待提交法定人数时,mongod 将从索引的集合中生成的任何其他键添加到侧写入表中,并定期清理该表。

锁定
mongod 将集合上的排他性IX锁升级为共享的S锁。这将阻止对集合的所有写入操作,包括针对集合的任何复制的写入操作或元数据命令。
完成处理临时侧写入表

mongod 继续清理侧写入表中的剩余记录。在这个阶段,可能会暂停复制。

如果mongod 在处理侧写入表中的键时遇到键生成错误,它会将该键存储在约束违规表中以供后续处理。

如果mongod 在处理键时遇到任何其他错误,构建将因错误而失败。

锁定
mongod 将集合上的共享S锁升级为排他的X锁。这将阻止对集合的所有读写操作,包括任何复制的写入操作或元数据命令。此锁不会被释放。
删除侧写入表

在删除之前,mongod 应用侧写入表中的任何剩余操作。

如果mongod 在处理侧写入表中的键时遇到键生成错误,它会将该键存储在约束违规表中以供后续处理。

如果mongod 在处理键时遇到任何其他错误,构建将因错误而失败。

此时,索引包含已写入集合的所有数据。

处理约束违规表

如果mongod 是主节点,则使用先进先出优先级清理约束违规表。

  • 如果约束违规表中的键没有生成键生成错误,或者该表为空,则mongod 将删除该表并创建一个“commitIndexBuild”操作记录。辅助节点可以在复制操作记录后完成相关的索引构建。

  • 如果约束违反表中任何键仍然产生键生成错误,mongod将终止构建并抛出错误。mongod创建一个相关的“abortIndexBuild”操作日志条目,以指示从节点应终止并丢弃索引构建作业。

如果mongod是一个从节点,它会丢弃约束违反表。由于主节点必须在创建“commitOplogEntry”操作日志条目之前成功清空约束违反表,因此从节点可以安全地假设不存在违反。

标记索引为就绪

mongod更新索引元数据,以标记索引为可用。

锁定
mongod释放集合上的X锁。

提示

另请参阅

返回

转换为唯一