理想的分片键允许MongoDB在整个集群中均匀分布文档,同时促进常见的查询模式。由于数据分布不均匀,次优的分片键可能会导致性能或扩展问题。您可以更改集合的分片键,以更改集群中的数据分布。
从MongoDB 8.0 开始,您可以对同一分片键上的集合重新分片,从而允许您在不更改分片键的情况下重新分发数据以包含新分片或不同区域。要重新分片为相同的分片键,请将 forceRedistribution 设立为 true。
从MongoDB 8.0.10 开始,您可以对时间序列集合重新分片。时间序列集合中的所有分片都必须运行8.0.10 或更高版本才能重新分片。
注意
在对集合重新分片之前,请阅读分片键疑难解答,了解有关常见性能和扩展问题的信息以及如何修复这些问题的建议。
关于此任务
一次只能对一个集合重新分片。
writeConcernMajorityJournalDefault必须为true。在重新分片操作正在进行时,正在重新分片的集合不支持以下命令和相应的 shell 方法:
如果运行以下操作之一,则该操作会等到重新分片完成后再执行:
dropDatabase在托管正在进行重新分集合的数据库上
如果您要重新分片的集合使用MongoDB Search,则在重新分片操作完成后,搜索索引将变为不可用。重新分片操作完成后,您需要手动重建搜索索引。
If the collection you're resharding is archived in Atlas Online Archives, the online archive files are marked as
Orphanedonce the resharding operation completes. You can create another online archive for the same database, collection, and fields as the orphaned archive as long as there is no other archive for that same combination in theActivestate.
开始之前
在开始对集合重新分片之前,请确保满足以下要求:
您的应用程序可以允许受影响的集合块进行两秒钟的写入。 在写入受阻期间,应用程序的延迟会增加。
如果您的工作负载无法允许此要求,请考虑改为改进分片键。
您的数据库符合这些资源要求:
确保每个接收分片上的可用存储空间至少是要重新分片的集合存储大小加上其总索引大小的两倍,再除以分片数:
( ( collection_storage_size + index_size ) * 2 ) / shard_count = storage_req 示例,考虑一个存储大小为 2 TB 数据和 400 GB 索引的集合。要将其分发到四个分片,您需要:
( ( 2 TB collection + 0.4 TB index ) * 2 ) / 4 shards = 1.2 TB storage 要对该集合重新分片,每个分片需要 1.2TB 的可用存储。
在MongoDB Atlas上,您可能需要升级到下一个层级的存储才能执行重新分片操作。操作完成后即可降级。
确保 I/O容量低于50 %。
确保 CPU 负载低于80 %。
重要
数据库不会强制执行这些要求。未能分配足够的资源可能会导致:
数据库空间不足并关闭
性能下降
操作花费的时间比预期长
如果应用程序存在流量较少的时间段,请尽可能在该时间段对集合执行此操作。
重新分片之前,无需在新分片键上创建索引。重新分片操作会在索引阶段自动构建所需的索引。
没有正在进行的索引构建。要检查是否正在运行索引构建,请使用
$currentOp:db.getSiblingDB("admin").aggregate( [ { $currentOp : { idleConnections: true } }, { $match: { $or: [ { "op": "command", "command.createIndexes": { $exists: true } }, { "op": "none", "msg": /^Index Build/ } ] } } ] ) 在结果文档中,如果
inprog字段值为空数组,则表示没有正在运行的索引构建:{ inprog: [], ok: 1, '$clusterTime': { ... }, operationTime: <timestamp> }
注意
重新分片操作是一个写入密集型进程,可以提高oplog的速率。您可能希望:
设置固定的 oplog 大小以防止 oplog 无限增长。
增加 oplog 大小以最大限度地减少一个或多个从节点过时的可能性。
有关更多详细信息,请参阅副本集 Oplog 文档。
两阶段写入协议
未在查询筛选条件中包含完整分片键的查询可能需要两阶段写入协议。该协议将查询广播到多个分片,并可能会显著降低性能。
提供旧分片键而非新分片键的查询将使用两阶段写入协议。
当查询筛选条件不使用完整的分片键或
_id字段时,deleteOne()方法使用二阶段写入协议。replaceOne()和updateOne()方法:当查询提供完整的分片键时,绝不会使用两阶段协议。
当查询未提供完整的分片键但提供
_id字段时,两阶段写入协议的使用取决于upsert的值。如果upsert设置为true,则使用两阶段写入协议。如果upsert为false,就不会使用两阶段写入协议。当查询既不提供完整的分片键,也不提供
_id字段时,始终使用两阶段写入协议。
当查询筛选条件未使用完整分片键时,以下方法会使用两阶段写入协议:
为获得最佳性能,在分片重新操作完成后更新您的应用程序,以使用新的分片键在查询筛选条件中进行这些操作。
步骤
在集合重新分片操作中,分片可以是:
发送分片,它目前存储分片集合的数据段。
分片可以同时是发送分片和接收分片。
配置服务器主节点始终是重新分片协调器,并启动重新分片操作的每个阶段。
禁用负载均衡器
You must turn off the balancer before you begin the process of resharding a collection. To disable the balancer, see here.
开始重新分片操作。
连接到 mongos 时,发出 reshardCollection 命令,指定要重新分片的集合和新分片键:
db.adminCommand({ reshardCollection: "<database>.<collection>", key: <shardkey> })
MongoDB 将阻止写入的最大秒数设置为两秒,并开始重新分片操作。
要重新分片为相同的分片键,请将 forceRedistribution 设立为 true:
db.adminCommand({ reshardCollection: "<database>.<collection>", key: <shardkey>, forceRedistribution: true })
您还可以使用 sh.reshardCollection() 对具有相同密钥的集合重新分片。有关示例,请参阅将数据重新分发到新分片。
监控重新分片操作。
要监控重分片操作,可以使用 $currentOp 管道阶段:
db.getSiblingDB("admin").aggregate([ { $currentOp: { allUsers: true, localOps: false } }, { $match: { type: "op", "originatingCommand.reshardCollection": "<database>.<collection>" } } ])
注意
要查看更新的值,您需要连续运行前面的管道。
$currentOp 管道输出:
totalOperationTimeElapsedSecs:经过的操作时间(以秒为单位)remainingOperationTimeEstimatedSecs: estimated time remaining in seconds for the current resharding operation. It is returned as-1when a new resharding operation starts.从MongoDB 7.0 开始,在重新分片操作期间,
remainingOperationTimeEstimatedSecs在协调器上也可用。remainingOperationTimeEstimatedSecs设置为悲观的时间估计值:将追赶阶段时间估计值设置为克隆阶段时间,这是一个相对较长的时间。
实际上,如果只有几个待处理的写入操作,则实际的追赶阶段时间相对较短。
[ { shard: '<shard>', type: 'op', desc: 'ReshardingRecipientService | ReshardingDonorService | ReshardingCoordinatorService <reshardingUUID>', op: 'command', ns: '<database>.<collection>', originatingCommand: { reshardCollection: '<database>.<collection>', key: <shardkey>, unique: <boolean>, collation: { locale: 'simple' } }, totalOperationTimeElapsedSecs: <number>, remainingOperationTimeEstimatedSecs: <number>, ... }, ... ]
重新启用负载均衡器。
To enable the balancer, see here.
行为
重新分片操作的最短持续时间
重新分片操作的最短持续时间始终为 5 分钟。
可重试写入 (Retryable Writes)
在重新分片之前或期间启动的可重试写入可以在集合重新分片期间和之后重试最多 5 分钟。5 分钟后,您可能无法找到写入的最终结果,并且后续尝试重试写入会失败,并引发 IncompleteTransactionHistory 错误。
重新分片限制
If the collection uses Atlas Search, the search index becomes unavailable after the operation completes. To restore it, manually rebuild the search index.
不支持使用可查询加密的集合。
错误案例
Duplicate _id Values
如果 _id 值不是全局唯一的,则重新分片操作会失败,以避免损坏集合数据。重复的 _id 值也会阻止成功的数据段迁移。如果您的文档具有重复的 _id 值,请将每个文档中的数据复制到新文档中,然后删除重复的文档。