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