Docs 菜单
Docs 主页
/
MongoDB Manual
/

已填充集合上的索引构建

在此页面上

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

索引构建使用优化的构建进程,该进程在索引构建的开始和结束时在集合上持有独占锁。构建进程的其余部分交叉执行读取和写入操作。有关索引构建进程和锁定行为的详细说明,请参阅 索引构建进程。

索引构建在副本集或分片集群上,并在所有数据承载副本集节点上同时构建。主节点要求最少数量的承载数据且有投票权的节点(即提交法定人数),包括其本身,必须先完成构建,然后才能将索引标记为可供使用。“投票权”节点是任何 members[n].votes 大于 0 的副本集节点。有关更多信息,请参阅 复制环境中的索引构建

从 MongoDB 7.1 开始,索引构建得到了改进,报告速度更快,韧性更强。您还可以使用新的indexBuildMinAvailableDiskSpaceMB参数设置索引构建所需的最小可用磁盘空间,如果磁盘空间太低,该参数会停止索引构建。

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

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

注意

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

MongoDB 的早期版本支持在前台或后台构建索引。前台索引构建速度快,可生成更高效的索引数据结构。但是,在为集合构建索引期间,需要阻止对其父数据库的所有读写访问。后台索引构建速度较慢,其生成的索引数据结构也不如前台构建的高效,但允许在构建过程中对数据库及其集合进行读写访问。

现在,索引构建仅会在构建流程的开始和结束期间对被索引的集合获取独占锁,从而保护元数据更改。构建流程的其余部分将使用后台索引构建的锁释放行为来最大化构建期间对集合的读写访问。尽管有更宽松的锁定行为,索引构建仍可生成高效的索引数据结构。

经优化的索引构建性能至少与后台索引构建相当。如果在构建过程中,工作负载内含有很少或不含更新操作,经优化的索引构建速度可以和对相同数据进行前台索引构建一样快。

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

如果为 createIndexes 或其 shell 助手createIndex()createIndexes() 指定了 background 索引构建选项,则 MongoDB 将忽略此选项。

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

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

同样,正在进行索引构建时,应用程序将能够成功地将具有重复的 product_sku 值的文档写入 inventory 集合。如果在构建结束时仍然存在任何违规行为,mongod 将终止构建并报告错误。

为了降低由于违反约束条件而导致索引构建失败的风险,请执行以下操作:

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

  • 停止所有可能违反索引约束条件的应用程序对集合的写入操作

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

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

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

  • 停止所有可能违反索引约束条件的应用程序对集合的写入操作

提示

另请参阅:

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

如果并发索引构建的数量达到 maxNumActiveUserIndexBuilds 指定的限值,服务器将阻止其他索引构建,直到并发索引构建的数量降至限值以下。

在目标集合处于大量写入负载的时间段内构建索引,可能会导致写入性能降低,且索引构建的时间将更长。

请考虑指定一个维护窗口,在此期间应用程序将停止或减少针对集合的写入操作。应在此维护窗口内启动索引构建,以降低构建过程中潜在的负面影响。

createIndexes 支持在集合上建立一个或多个索引。createIndexes 使用磁盘上的内存和临时文件的组合来完成索引构建。createIndexes 的默认内存使用限制为 200 MB,内存在使用单个 createIndexes 命令构建的所有索引之间共享。达到内存限制后,createIndexes 将使用 --dbpath 目录中名为 _tmp 的子目录中的临时磁盘文件来完成构建。

您可以通过设置 maxIndexBuildMemoryUsageMegabytes 服务器参数来覆盖内存限制。设置更高的内存限制可能会加快完成索引构建过程。但是,相对于系统上未使用的 RAM 而言,将此限制设置得太高可能会导致内存耗尽和服务器关闭。

如果主机的可用空闲 RAM 有限,则可能需要安排维护期以增加系统总 RAM,然后才能修改 mongod RAM 的使用量。

注意

需要 featureCompatibilityVersion 4.4+

副本集或分片集群中的每个 mongod 必须featureCompatibilityVersion 设置为至少 4.4,才能跨副本集节点同时启动索引构建。

索引构建在副本集或分片集群上,并在所有数据承载副本集成员上同时构建。对于分片集群,索引构建仅会在包含当前被索引集合的数据的分片上进行。主节点需要最少数量的数据承载 voting 节点(即提交法定节点数),包括其自身,这些节点必须在将索引标记为可供使用之前完成构建。

重要

如果无法访问承载数据的投票节点,并且将commitQuorum设立为默认votingMembers ,则索引构建可能会挂起,直到该节点重新在线。

构建过程总结如下:

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

  2. 从节点 (secondary node from replica set) 在复制 “startIndexBuild” oplog 条目后开始索引构建过程。

  3. 每个节点完成为集合中数据构建索引后,将通过“投票”来提交构建的索引。

  4. 从节点会继续处理针对索引的所有新写入操作,同时等待主节点确认投票数量是否达到定额。

  5. 当主节点获得足量的投票时,它会检查任何违反键约束的情况,例如重复键错误。

    • 如果没有违反键约束的情况出现,主节点会完成索引构建,将索引标记为可用,并创建相关的“commitIndexBuild” oplog 条目。

    • 如果出现任何违反键约束的情况,索引构建将失败。主节点将中止索引构建并创建相关的“abortIndexBuild” oplog 条目。

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

    如果从节点复制的是“abortIndexBuild” oplog 条目,它们会中止索引构建并放弃构建作业。

对于分片集群环境,在为其中的集合构建索引时,索引构建过程仅在含有该集合数据的分片上发生。

有关索引构建流程的更详细说明,请参阅索引构建流程

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

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

注意

索引构建可能会影响副本集性能。对于无法容忍因索引构建而导致性能下降的工作负载,请考虑进行滚动索引构建。滚动索引构建一次最多选取一个副本集节点(始于从节点),使其暂时作为独立运行的实例进行索引构建。滚动索引构建需要进行至少一次副本集选举。

提交法定人数写关注之间有重要区别:

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

  • 写入操作使用写关注。

集群中的每个承载数据的节点均为一个有投票权成员。

提交法定人数指定了必须准备多少个承载数据的有投票权节点,或是哪些有投票权节点(包括主节点)来提交同步索引构建,主节点才会执行提交。

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

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

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

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

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

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

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

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

从 MongoDB 5.0 开始,如果在索引构建期间某一节点回滚到之前的状态,则会将索引构建进度保存到磁盘。如果回滚结束时仍有工作要做,mongod 则会自动恢复索引构建,并从保存的检查点继续运行。

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

  • 如果回滚没有撤销索引构建进度,MongoDB 会在完成回滚后重新启动索引构建作业。

  • 如果回滚操作撤销了索引构建进度,您必须在回滚完成后重新创建一个或多个索引。

如果一个分片集合在包含集合数据块的每个分片上没有完全相同的索引(包括索引选项),则该集合的索引不一致。尽管一般操作过程中不应出现索引不一致的情况,但这种情况仍有可能发生,例如:

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

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

要检查分片集合是否存在不一致的索引,请参阅查找分片间不一致的索引

要查看索引构建操作的状态,可以使用 mongosh 中的 db.currentOp() 方法。要过滤索引创建操作的当前操作,请参阅主动创建索引的操作,查看相关示例。

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 在此初始状态下创建了以下三个数据结构:

  • 初始索引元数据条目。

  • 一张临时表(“侧写表”),用于存储在构建过程中,对被索引的集合进行写入时生成的键。

  • 临时表(“约束违规表”),用于显示可能导致密钥生成错误的所有文档。当文档的索引字段具有无效键时,会发生键生成错误。例如,在构建唯一索引时出现重复字段值的文档,在构建 2dsphere 索引时出现格式错误的 GeoJSON 对象

mongod 将独占 X 集合锁降级为意向独占 IX 锁。mongod 会定期生成此锁以交替执行读取和写入操作。
扫描集合

对于集合中的每个文档,mongod 会为该文档生成密钥并将密钥转储到外部排序器中。

如果 mongod 在集合扫描期间生成密钥时遇到密钥生成错误,则会将密钥存储在约束违规表中以供日后处理。

如果 mongod 在生成键时遇到任何其他错误,则构建过程会失败并显示错误。

一旦 mongod 完成集合扫描,它就会将排序后的键转储到索引中。

处理侧写入表

mongod 使用先入先出的优先级顺序清空侧写入表。

如果 mongod 在处理旁写表中的键时出现键生成错误,则会将该密钥存储在约束违规表中以供日后处理。

如果 mongod 在处理键时遇到任何其他错误,则构建过程将失败并显示错误。

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

投票并等待提交法定人数

属于副本集的 mongod 会跳过此阶段。

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

如果 mongod主节点,它会等到达到投票提交法定人数(默认情况下所有投票数据承载成员),然后再继续索引构建进程。

如果 mongod从节点,它将一直等到复制“commitIndexBuild”或“abortIndexBuild”oplog 条目为止:

  • 如果 mongod 复制“commitIndexBuild”oplog 条目,它会排空侧写入表,并进入索引构建过程的下一阶段。

  • 如果 mongod 复制“abortIndexBuild”oplog 条目,它将中止索引构建并放弃构建作业。

在等待提交法定人数时,mongod 会将写入到正在构建索引的集合的操作生成的所有附加键添加到侧写入表,并定期清空该表。

mongod 将集合上的意向独占 IX 锁升级为共享 S 锁。这会阻塞针对集合的所有写入操作,包括应用任何复制的写入操作或针对集合的元数据命令。
完成处理临时侧写入表

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

如果 mongod 在处理旁写表中的键时出现键生成错误,则会将该密钥存储在约束违规表中以供日后处理。

如果 mongod 在处理键时遇到任何其他错误,则构建过程将失败并显示错误。

mongod 将集合上的共享 S 锁升级为集合上的独占 X 锁。这会阻塞集合上的所有读取和写入操作,包括应用任何复制的写入操作或针对集合的元数据命令。mongod 不会生成此锁。
删除侧写入表

mongod 在删除侧写入表之前应用该表中的所有剩余操作。

如果 mongod 在处理旁写表中的键时出现键生成错误,则会将该密钥存储在约束违规表中以供日后处理。

如果 mongod 在处理键时遇到任何其他错误,则构建过程将失败并显示错误。

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

进程约束违规表

如果 mongod主节点,则会按照先入先出的优先级顺序清空约束违规表。

  • 如果约束违规表中没有键产生键生成错误该表为空,则 mongod 会删除该表并创建 "commitIndexBuild" oplog 条目。从节点可以在复制 oplog 条目后完成关联的索引构建。

  • 如果约束违规表中有某个键仍会产生键生成错误,则 mongod 将中止构建并报告错误。mongod 创建关联的 "abortIndexBuild" oplog 条目,指示从节点应中止并放弃索引构建作业。

如果 mongod从节点,则会删除约束违规表。由于主节点必须在创建 "commitOplogEntry" oplog 条目之前成功清空约束违规表,因此从节点可以安全地假设不存在违规。

将索引标记为可供使用

mongod 会更新索引元数据以将索引标记为可供使用。

mongod 将释放集合上的 X 锁。

提示

另请参阅:

后退

转换为唯一