在已填充集合上构建索引
索引构建使用优化的构建过程,在索引构建的开始和结束时对集合持有独占锁。其余构建过程则允许交错读取和写入操作。有关索引构建过程和锁定行为的详细说明,请参阅索引构建过程.
在副本集或分片集群上构建索引将在所有承载数据的数据副本集成员上同时进行。主节点需要完成构建前必须完成的最小数据承载投票成员数(即提交法定人数),包括自身。任何投票成员是指任何副本集成员,其中members[n].votes
大于 0
。有关更多信息,请参阅副本环境中的索引构建。
从 MongoDB 7.1 开始,索引构建得到改进,具有更快的错误报告和更高的故障恢复能力。您还可以使用新的 indexBuildMinAvailableDiskSpaceMB
参数设置索引构建所需的最低可用磁盘空间,如果磁盘空间过低,则停止索引构建。
以下表格比较了从 MongoDB 7.1 开始的索引构建行为与早期版本的行为。
从 MongoDB 7.1 开始的行为 | 早期 MongoDB 版本中的行为 |
---|---|
在集合扫描阶段发现的索引错误(除了重复键错误外)将立即返回,然后停止索引构建。早期 MongoDB 版本在提交阶段返回错误,该阶段发生在索引构建的接近结束时。MongoDB 7.1 帮助您快速诊断索引错误。例如,如果找到不兼容的索引值格式,错误将立即返回给您。 | 与 MongoDB 7.1 相比,索引构建错误可能需要较长时间才能返回,因为错误是在提交阶段、索引构建接近结束时返回的。 |
索引构建错误可能导致次要成员崩溃。 | |
改进了索引构建的磁盘空间管理。如果可用磁盘空间低于 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
指定的限制,服务器将阻止进一步的索引构建,直到并发索引构建的数量低于限制。
索引构建对数据库性能的影响
在写密集型工作负载期间进行索引构建
在目标集合处于高写负载的时间段内建立索引可能会导致写性能降低和索引建立时间更长。
考虑指定一个维护窗口,在此期间应用程序停止或减少对集合的写操作。在此维护窗口内开始索引建立,以减轻建立过程可能产生的负面影响。
系统可用内存(RAM)不足
createIndexes
支持在一个集合上建立一个或多个索引。 createIndexes
使用内存和磁盘上的临时文件组合来完成索引建立。 createIndexes
默认的内存使用限制为 200 兆字节,由使用单个 createIndexes
命令建立的索引共享。一旦达到内存限制,createIndexes
将使用位于 --dbpath
目录下名为 _tmp
的子目录中的临时磁盘文件来完成建立。
您可以通过设置 maxIndexBuildMemoryUsageMegabytes
服务器参数来覆盖内存限制。设置更高的内存限制可能会导致索引建立更快完成。然而,如果与系统未使用的 RAM 相比设置得太高,可能会导致内存耗尽和服务器关闭。
如果主机机器可用的空闲 RAM 有限,您可能需要在修改 mongod
RAM 使用之前安排一个维护周期来增加总系统 RAM。
在复制环境中的索引建立
在副本集或分片集群上构建的索引会在所有承载数据的副本集成员之间同时进行。对于分片集群,索引构建仅发生在包含正在索引的集合数据的分片上。主节点需要一定数量的承载数据的 投票
成员(即提交法定人数),包括自身,必须在标记索引为就绪使用之前完成构建。
重要
如果承载数据的投票节点变得不可达,并且将 提交法定人数 设置为默认的 votingMembers
,则索引构建可能会挂起,直到该节点重新上线。
构建过程总结如下:
主节点接收到
createIndexes
命令并立即创建与索引构建关联的 "startIndexBuild" oplog 条目。从节点在复制 "startIndexBuild" oplog 条目后开始索引构建。
每个成员在完成索引集合中的数据索引后“投票”提交构建。
从节点继续处理任何新的写入操作到索引,同时在等待主节点确认法定人数的投票。
当主节点拥有法定人数的投票时,它会检查任何键约束违规,例如重复键错误。
如果没有键约束违规,主节点完成索引构建,标记索引为就绪使用,并创建相关的 "commitIndexBuild" oplog 条目。
如果有任何键约束违规,则索引构建失败。主节点中止索引构建并创建相关的 "abortIndexBuild" oplog 条目。
从节点复制 "commitIndexBuild" oplog 条目并完成索引构建。
如果从节点复制的是 "abortIndexBuild" oplog 条目,则它们中止索引构建并丢弃构建作业。
对于分片集群,索引构建仅发生在包含正在索引的集合数据的分片上。
有关索引构建过程的更详细描述,请参阅 索引构建过程。
默认情况下,索引构建使用提交法定人数为 "votingMembers"
,即所有承载数据的投票成员。要使用非默认的提交法定人数启动索引构建,请将 提交法定人数 参数指定为 createIndexes
或其 shell 辅助程序 db.collection.createIndex()
和 db.collection.createIndexes()
。
要修改正在进行的同时索引构建所需的提交法定人数,请使用 setIndexCommitQuorum
命令。
注意
索引构建可能会影响副本集的性能。对于无法忍受性能下降的索引构建工作负载,考虑执行滚动索引构建过程。滚动索引构建每次最多使一个副本集成员退出,从次要成员开始,在该成员上独立构建索引。滚动索引构建至少需要一个副本集选举。
有关副本集上的滚动索引构建,请参阅副本集上的滚动索引构建。
有关分片集群上的滚动索引构建,请参阅分片集群上的滚动索引构建。
提交法定票数与写关注点
提交法定票数与写关注点之间有重要差异:
索引构建使用提交法定票数。
写操作使用写关注点。
集群中每个承载数据的节点都是一个投票成员。
提交法定票数指定了在主节点执行提交之前,必须有多少个承载数据的投票成员,或者哪些投票成员(包括主节点)必须准备好提交一个同时索引构建。
写关注点是写操作传播到指定数量实例的确认级别。
更改版本8.0: 提交法定票数指定了在主节点提交索引构建之前,必须有多少个节点准备好完成索引构建。相比之下,当主节点提交索引构建时,写关注点指定了在命令返回成功之前,必须有多少个节点复制索引构建操作日志条目。
在以前的版本中,当主节点提交索引构建时,写关注点指定了在命令返回成功之前,必须有多少个节点完成索引构建。
构建失败和恢复
主节点 mongod
上中断的索引构建
从 MongoDB 5.0 开始,如果主节点 mongod
在索引构建期间进行干净的 shutdown
并带有 "force" : true
或接收到 SIGTERM
信号,并且 commitQuorum 设置为默认的 votingMembers
,则索引构建进度将被保存到磁盘。当 mongod
重新启动时,它会自动恢复索引构建,并从保存的检查点继续。在早期版本中,如果索引构建被中断,则必须从头开始重新启动。
辅助节点 mongod
上中断的索引构建
从MongoDB 5.0版本开始,如果一个辅助的 mongod
在进行索引构建过程中执行了一个干净的 shutdown
命令并带有 "force" : true
或接收了 SIGTERM
信号,并且 commitQuorum 设置为默认的 votingMembers
,那么索引构建的进度将保存到磁盘。当 mongod
重新启动时,它会自动恢复索引构建并从保存的检查点继续。在早期版本中,如果索引构建被中断,必须从头开始重新启动。
mongod
可以在恢复索引构建的过程中执行启动过程。
如果您以独立模式(即删除或注释掉 replication.replSetName
或省略 --replSetName
)重启 mongod
,则 mongod
无法重新启动索引构建。构建将保持暂停状态,直到手动 删除
。
独立 mongod
的中断索引构建
如果在索引构建过程中 mongod
关闭,则索引构建作业和所有进度都将丢失。重新启动 mongod
不会重新启动索引构建。您必须重新执行 createIndex()
操作来重新启动索引构建。
构建过程中的回滚
从MongoDB 5.0开始,如果在索引构建过程中节点回滚到先前状态,则将索引构建进度保存到磁盘。如果回滚完成后还有工作要做,mongod
将自动恢复索引构建并从保存的检查点继续。
MongoDB可以暂停正在进行的索引构建以执行回滚。
如果回滚没有撤销索引构建,MongoDB在完成回滚后重新开始索引构建。
如果回滚撤销了索引构建,则必须在回滚完成后重新创建索引或索引。
分片集合的索引一致性检查
如果分片集合中包含该集合数据分片的每个分片没有完全相同的索引(包括索引选项),则该分片集合的索引是不一致的。尽管在正常操作期间不应出现不一致的索引,但可能会发生,例如
当用户使用具有
unique
键约束创建索引时,其中一个分片包含具有重复文档的数据块。在这种情况下,可能在没有重复的数据块的分片上成功执行创建索引操作,但在包含重复的数据块的分片上失败。当用户以滚动方式(即手动逐个在分片上构建索引)创建分片之间的索引,但未能为关联的分片构建索引或以不同的规范错误地构建索引。
分片集合的主配置服务器定期检查分片之间的索引一致性。要配置这些定期检查,请参阅enableShardedIndexConsistencyCheck
和shardedIndexConsistencyCheckIntervalMS
。
命令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 将排他性的 X 集合锁降级为意向排他性 IX 锁。mongod 会定期释放此锁,以交错读和写操作。 |
扫描集合 | 对于集合中的每个文档, 如果在集合扫描过程中生成键时遇到键生成错误,则将其存储在约束违反表中进行后续处理。 如果在生成键时遇到任何其他错误,则构建失败并出现错误。 一旦 |
进程侧写入表 |
如果 如果 在构建过程中,对于写入到集合中的每个文档, |
投票并等待提交法定人数 | 不属于复制集的
如果 如果
在等待提交法定人数时, |
锁定 | mongod 将集合上的排他性IX 锁升级为共享的S 锁。这将阻止对集合的所有写入操作,包括针对集合的任何复制的写入操作或元数据命令。 |
完成处理临时侧写入表 |
如果 如果 |
锁定 | mongod 将集合上的共享S 锁升级为排他的X 锁。这将阻止对集合的所有读写操作,包括任何复制的写入操作或元数据命令。此锁不会被释放。 |
删除侧写入表 | 在删除之前, 如果 如果 此时,索引包含已写入集合的所有数据。 |
处理约束违规表 | 如果
如果 |
标记索引为就绪 |
|
锁定 | mongod 释放集合上的X 锁。 |