文档菜单
文档首页
/
MongoDB 手册
/ / /

重新分片集合

本页面上

  • 关于此任务
  • 开始之前
  • 步骤
  • 启动重新分片操作。
  • 监控重新分片操作。
  • 完成重新分片操作。
  • 尽早阻止写入以强制完成重新分片
  • 中止重新分片操作
  • 行为
  • 重新分片操作的最短持续时间
  • 可重试写入
  • 错误情况
  • 重复_id

版本5.0.

理想分片键允许MongoDB在集群中均匀分布文档,同时促进常见的查询模式。子优化的分片键可能导致性能或扩展问题,因为数据分布不均匀。

从MongoDB 5.0开始,您可以更改集合的分片键,以更改集群中数据分布。

从MongoDB 8.0开始,您可以在同一分片键上重新分片集合,允许您重新分配数据以包括新分片或不同的区域,而无需更改分片键。

注意

在重新分片您的集合之前,请阅读解决分片键问题有关常见性能和扩展问题以及如何修复它们的信息。

在您开始重分片集合之前,请确保满足以下要求

  • 您的应用程序可以容忍在受影响的集合阻止写入期间出现 两秒钟 的时间。在此期间,您的应用程序会经历延迟增加。

    如果您的负载无法容忍此要求,请考虑优化您的分片键

  • 您的数据库满足以下资源要求

    • 确保将集合将分布到的每个分片上可用的存储空间至少是您想要重分片的集合及其总索引大小除以分片数量的两倍。

      storage_req = ( ( collection_storage_size + index_size ) * 2 ) / shard_count

      例如,考虑一个包含 2 TB 数据并具有 400 GB 索引分布在四个分片上的集合。要在此集合上执行重分片操作,每个分片就需要 1.2 TB 的可用存储。

      1.2 TB storage = ( ( 2 TB collection + 0.4 TB index ) * 2 ) / 4 shards

      为了满足存储要求,您可能需要在重分片操作期间升级到下一个存储级别。操作完成后,您可以降级。

    • 确保您的 I/O 容量低于 50%。

    • 确保您的 CPU 负载低于 80%。

    重要

    数据库不会强制执行这些要求。无法分配足够的资源可能导致

    • 数据库空间不足并关闭

    • 性能下降

    • 操作时间比预期更长

    如果您的应用程序在交通较少的时间段,请在可能的情况下在此期间对集合执行此操作。

  • 您必须重新编写应用程序的查询以使用 当前 分片键和 分片键。

    提示:

    如果您的应用程序可以容忍中断,您可以通过以下步骤避免重新编写应用程序的查询以使用当前和新的分片键

    1. 停止您的应用程序。

    2. 将应用程序重写为使用 分片键。

    3. 等待重分片完成。要监控重分片过程,请使用 $currentOp 管道阶段。

    4. 部署您重写的应用程序。

    在重分片完成之前,如果查询过滤器不包含当前分片键或唯一字段(如 _id),以下查询将返回错误

    为了最佳性能,我们建议您也将其他查询重写为包含新的分片键。

    一旦重新分片操作完成,您可以从查询中删除旧的分片键。

  • 没有索引构建正在进行。使用 db.currentOp() 检查是否有正在进行的索引构建

    db.adminCommand(
    {
    currentOp: true,
    $or: [
    { op: "command", "command.createIndexes": { $exists: true } },
    { op: "none", "msg" : /^Index Build/ }
    ]
    }
    )

    在结果文档中,如果 inprog 字段的值是一个空数组,则没有正在进行的索引构建

    {
    inprog: [],
    ok: 1,
    '$clusterTime': { ... },
    operationTime: <timestamp>
    }

注意

重新分片是一个写入密集型过程,可能会产生增加的操作日志速率。您可能希望

  • 设置一个固定的操作日志大小,以防止无界的操作日志增长。

  • 增加操作日志大小以最小化一个或多个辅助节点过时的可能性。

有关更多详细信息,请参阅副本集操作日志文档。

重要

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

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

  • 捐赠者,当前存储分片集合的数据块

  • 接收者,根据分片键区域存储分片集合的新数据块。

一个分片可以同时是捐赠者和接收者。除非您使用区域,否则捐赠者分片集与接收者分片集相同。

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

1

连接到 mongos 后,执行一个 reshardCollection 命令,指定要重新分片的集合和新的分片键

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

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

2

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

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

注意

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

$currentOp 管道输出

  • totalOperationTimeElapsedSecs:操作的经过时间(秒)

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

    • MongoDB 5.0 开始,但在 MongoDB 7.0 之前,remainingOperationTimeEstimatedSecs 只在重新分片操作期间在 接收分片 上可用。

    • MongoDB 7.0,remainingOperationTimeEstimatedSecs 在重新分片操作期间也在协调器上可用。

    重新分片操作按以下顺序执行这些阶段

    1. 克隆阶段复制当前集合数据。

    2. 追赶阶段将任何待处理的写操作应用于重新分片的集合。

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

在整个重新分片过程中,完成重新分片操作的估计时间(remainingOperationTimeEstimatedSecs)逐渐减少。当估计时间低于两秒时,MongoDB会阻止写操作并完成重新分片操作。在估计时间低于两秒之前,默认情况下,重新分片操作不会阻止写操作。在写操作被阻止的时间段内,您的应用程序将经历延迟的增加。

一旦重新分片过程完成,重新分片命令返回ok: 1

{
ok: 1,
'$clusterTime': {
clusterTime: <timestamp>,
signature: {
hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
keyId: <number>
}
},
operationTime: <timestamp>
}

要查看重新分片操作是否成功完成,请检查sh.status()方法的结果。

sh.status()

sh.status()方法输出包含一个关于databases的子部分。如果重新分片成功完成,输出将列出集合的新分片键。

databases
[
{
database: {
_id: '<database>',
primary: '<shard>',
version: {
uuid: <uuid>,
timestamp: <timestamp>,
lastMod: <number>
}
},
collections: {
'<database>.<collection>': {
shardKey: <shardkey>,
unique: <boolean>,
balancing: <boolean>,
chunks: [],
tags: []
}
}
}
...
]

注意

如果重新分片集合使用Atlas Search,则当重新分片操作完成时,搜索索引将不可用。您需要在重新分片操作完成后手动重建搜索索引。

您可以通过发出 commitReshardCollection 命令手动强制resharding操作完成。如果当前完成resharding操作的时间估计是可以接受的,那么这很有用,这样您的集合就可以阻塞写入。在关键区间的持续时间中,commitReshardCollection 命令会阻塞写入并强制resharding操作完成。该命令具有以下语法

db.adminCommand({
commitReshardCollection: "<database>.<collection>"
})

您可以在resharding操作的任何阶段中止resharding操作,甚至在运行 commitReshardCollection 之后,直到分片完全同步。

例如,如果 remainingOperationTimeEstimatedSecs 不减少,您可以使用 abortReshardCollection 命令中止resharding操作

db.adminCommand({
abortReshardCollection: "<database>.<collection>"
})

在取消操作后,您可以在低写入量的时间窗口中重试resharding操作。如果这不可能,在重试之前 添加更多分片

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

可重试的写入在resharding之前或过程中启动的写入可以在resharding完成后5分钟内重试。5分钟后,您可能无法找到写入的最终结果,并且重试写入尝试将因IncompleteTransactionHistory错误而失败。

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

返回

优化分片键