对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

对集合重新分片

理想的分片键允许MongoDB在整个集群中均匀分布文档,同时促进常见的查询模式。由于数据分布不均匀,次优的分片键可能会导致性能或扩展问题。您可以更改集合的分片键,以更改集群中的数据分布。

从MongoDB 8.0 开始,您可以对同一分片键上的集合重新分片,从而允许您在不更改分片键的情况下重新分发数据以包含新分片或不同区域。要重新分片为相同的分片键,请将 forceRedistribution 设立为 true。

从MongoDB 8.0.10 开始,您可以对时间序列集合重新分片。时间序列集合中的所有分片都必须运行8.0.10 或更高版本才能重新分片。

注意

在对集合重新分片之前,请阅读分片键疑难解答,了解有关常见性能和扩展问题的信息以及如何修复这些问题的建议。

在对集合重新分片之前,请确保满足以下要求:

  • 您的应用程序可以允许受影响的集合块进行两秒钟的写入。 在写入受阻期间,应用程序的延迟会增加。

    如果您的工作负载无法允许此要求,请考虑改为改进分片键。

  • 您的数据库符合这些资源要求:

    • 确保每个接收分片上的可用存储空间至少是要重新分片的集合存储大小加上其总索引大小的两倍,再除以分片数:

      ( ( 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 文档。

在查询过滤中不包含完整分片键的查询可能需要两阶段写入协议。此协议会将查询查询到多个分片,可能会显着降低性能。

  • 提供旧分片键而不是新分片键的查询使用两阶段写入协议。

  • replaceOne() 和 updateOne() 方法:

    • 当查询提供完整的分片键时,从不使用两阶段协议。

      • 当查询未提供完整分片键但提供 _id字段时,是否使用两阶段写入协议取决于 upsert 的值。如果将 upsert设立为 true,则使用两阶段写入协议。如果 upsert 为 false,则不使用两阶段写入协议。

      • 当查询既未提供完整的分片键,也未提供 _id字段时,则始终使用两阶段写入协议。

  • 当查询过滤不使用完整分片键时,以下方法使用两阶段写入协议:

为获得最佳性能,请在重新分片操作完成后更新应用程序,在这些操作的查询筛选器中使用新的分片键。

重要

我们强烈建议您在对集合重新分片之前完整查看“关于此任务”并阅读“步骤”部分。

在集合重新分片操作中,分片可以是:

  • 发送分片,它目前存储分片集合的数据段。

  • 接收分片,它根据分片键和区域存储分片集合的新数据段。

分片可以同时是发送分片和接收分片。

配置服务器主节点始终是重新分片协调器,并启动重新分片操作的每个阶段。

1

在开始对集合进行重新分进程之前,您必须关闭负载均衡器。 要禁用负载均衡器,请参阅此处。

2

连接到 mongos 时,发出 reshardCollection 命令,指定要重新分片的集合和新分片键:

db.adminCommand({
reshardCollection: "<database>.<collection>",
key: <shardkey>
})

MongoDB 将阻止写入的最大秒数设置为两秒,并开始重新分片操作。

要重新分片为相同的分片键,请将 forceRedistribution 设立为 true:

db.adminCommand({
reshardCollection: "<database>.<collection>",
key: <shardkey>,
forceRedistribution: true
})

您还可以使用 sh.reshardCollection() 对具有相同密钥的集合重新分片。有关示例,请参阅将数据重新分发到新分片。

3

要监控重分片操作,可以使用 $currentOp 管道阶段:

db.getSiblingDB("admin").aggregate([
{ $currentOp: { allUsers: true, localOps: false } },
{
$match: {
type: "op",
"originatingCommand.reshardCollection": "<database>.<collection>"
}
}
])

注意

要查看更新的值,您需要连续运行前面的管道。

$currentOp 管道输出:

  • totalOperationTimeElapsedSecs:经过的操作时间(以秒为单位)

  • remainingOperationTimeEstimatedSecs:当前重新分片操作的预计剩余时间(以秒为单位)。当新的重新分片操作开始时,将返回 -1。

    从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>,
...
},
...
]
4

要启用负载均衡器,请参阅此处。

重新分片操作的最短持续时间始终为 5 分钟。

在重新分片之前或期间启动的可重试写入可以在集合重新分片期间和之后重试最多 5 分钟。5 分钟后,您可能无法找到写入的最终结果,并且后续尝试重试写入会失败,并引发 IncompleteTransactionHistory 错误。

  • 如果集合使用Atlas Search,则在操作完成后,搜索索引将变为不可用。要恢复索引,请手动重建搜索索引。

  • 不支持使用可查询加密的集合。

如果 _id 值不是全局唯一的,则重新分片操作会失败,以避免损坏集合数据。重复的 _id 值也会阻止成功的数据段迁移。如果您的文档具有重复的 _id 值,请将每个文档中的数据复制到新文档中,然后删除重复的文档。