如何确认增加或删除Shard节点的进度

本文介绍如何查看增加Shard节点或删除Shard节点的任务进度,以及检查是否有异常阻塞任务进程的方法。

背景信息

当您对分片集群实例执行增加Shard节点或删除Shard节点操作后,可能会出现任务提交后很长时间都没完成的情况,以下内容将指导您如何检查上述问题。

在进行操作前,您需要了解以下基本知识:

  • 了解MongoDB副本集实例和分片集群实例的部署模式以及两种架构之间的区别。更多介绍,请参见副本集架构分片集群架构

  • 了解均衡器Balancer的基本工作原理。更多介绍,请参见管理MongoDB均衡器Balancer

  • 了解MongoDB数据的划分方式,MongoDB的数据是根据Chunk(块)进行划分的。

  • 了解MongoDB分片集群实例常见的运维命令,例如sh.status()等。

  • 了解mongo shell、mongosh或其他可视化工具的基础使用方式。

检查任务进程

步骤一:检查Balancer是否开启

在增加或删除分片任务中,Chunk的迁移是依赖Balancer完成的。如果未开启Balancer则会导致如下情况:

  • 新增Shard节点:Chunk数据无法迁移到新的Shard节点上,同时也无法承载业务流量。

  • 删除Shard节点:待删除Shard节点上的Chunk数据无法迁移,导致删除Shard节点的任务被阻塞。

因此,您需要开启Balancer来保障Chunk数据的正常迁移。开启Balancer的方法,请参见管理MongoDB均衡器Balancer

确认Balancer是否开启有如下两种方法:

  • 方法一:sh.status()命令

    Balancer已开启的状态下,返回示例如下。

    ...
      autosplit:
            Currently enabled: yes
      balancer:
            Currently enabled:  yes
            Currently running:  yes
                    Balancer active window is set between 08:30 and 11:30 server local time
    ...

    如果显示“Currently enabled: no”,则表示Balancer未开启。

  • 方法二:sh.getBalancerState()命令

    • 如果返回true,则表示已开启Balancer。

    • 如果返回false,则表示未开启Balancer。

步骤二:检查Balancer窗口期是否过小

Balancer控制了Chunk数据迁移的速度,Balancer仅会在窗口期时间段内迁移Chunk数据,若当前窗口期未迁移完成,会在下个窗口期继续迁移,直至迁移完成。当窗口期过小时,可能会拖慢增加Shard节点或删除Shard节点任务流的整体进度。调整Balancer窗口期的方法,请参见管理MongoDB均衡器Balancer

您可以通过如下两种方法检查Balancer的窗口期:

  • 方法一:sh.status()命令

    返回示例如下,可以了解到Balancer的窗口期为当地时间的08:3011:30,共计3个小时。

    ...
      autosplit:
            Currently enabled: yes
      balancer:
            Currently enabled:  yes
            Currently running:  yes
                    Balancer active window is set between 08:30 and 11:30 server local time
    ...
  • 方法二:sh.getBalancerWindow()命令

    返回示例如下,当实例中的分片集合较多时,采用此方法能够更直观的显示窗口期时间。

    { "start" : "08:30", "stop" : "11:30" }

步骤三:获取预估任务进度的必要信息

评估任务进度前,您需要获取Balancer的运行结果(成功和失败数量的统计)以及待迁移分片表的Chunk信息。

您可以通过如下两种方法获取Balancer的运行结果和待迁移分片表Chunk信息:

  • 方法一:通过sh.status()命令的输出结果获取

    sh.status()命令的输出结果仅需要关注两部分信息,第一部分为最近时间Balancer运行的结果,示例如下。

    ...
      balancer:
            Collections with active migrations: 
                    <db>.<collection> started at Wed Sep 27 2023 10:25:21 GMT+0800 (CST)
            Failed balancer rounds in last 5 attempts:  0
            Migration Results for the last 24 hours: 
                    300 : Success
      databases:
    ...

    第二部分为待迁移的分片表的Chunk信息,示例如下。

    ...
    databases:
    ...
        {  "_id" : "<db>",  "primary" : "d-xxxxxxxxxxxxxxx3",  "partitioned" : true,  "version" : {  "uuid" : UUID("3409a337-c370-4425-ad72-8b8c6b0abd52"),  "lastMod" : 1 } }
              <db>.<collection>
                            shard key: { "<shard_key>" : "hashed" }
                            unique: false
                            balancing: true
                            chunks:
                                    d-xxxxxxxxxxxxxxx1      13630
                                    d-xxxxxxxxxxxxxxx2      13629
                                    d-xxxxxxxxxxxxxxx3      13652
                                    d-xxxxxxxxxxxxxxx4      13630
                                    d-xxxxxxxxxxxxxxx5      3719
                            too many chunks to print, use verbose if you want to force print
    ...

    以上示例中的“d-xxxxxxxxxxxxxxx5”即为新增的Shard节点。您也可以在分区表所在的库执行getShardDistribution命令来获取Chunk数据的分布信息,示例如下。

    use <db>
    db.<collection>.getShardDistribution()
  • 方法二:直接读取config库中的相关信息

    查看Chunks统计,按分片聚合,示例如下。

    db.getSiblingDB("config").chunks.aggregate([{$group: {_id: "$shard", count: {$sum: 1}}}])

    查看指定分片上的Chunks,按namespace聚合,示例如下。

    db.getSiblingDB("config").chunks.aggregate([{$match: {shard: "d-xxxxxxxxxxxxxx"}},{$group: {_id: "$ns", count: {$sum: 1}}}])

    查看过去1天内成功迁移到指定Shard节点的Chunk数量,示例如下。

    // details.to指定chunk迁移的目标shard
    // time字段用ISODate指定时间范围
    db.getSiblingDB("config").changelog.find({"what" : "moveChunk.commit", "details.to" : "d-xxxxxxxxxxxxx","time" : {"$gte": ISODate("2023-09-26T00:00:00")}}).count()

步骤四:预估任务进度及完成时间

获取分片表已成功迁移的Chunk数量以及当前分片表数据分布情况后,您就可以预估出任务的整体进度和预期完成时间。

假如当前增加Shard节点的场景中,总Chunk数量保持不变,Shard节点数量也保持不变(期间不会有新增或删除Shard节点的任务),增加Shard节点的过程中业务负载基本保持固定,且Balancer的参数设置均为默认值。以上述步骤的示例为例,可以得知以下信息:

  • Shard节点数量为5。

  • Balancer窗口期内,成功迁移的Chunk数量为300个。

  • 分片表<db>.<collection>的总Chunk数量为58260个(13630+13629+13652+13630+3719=58260)。

因此可以计算出:

  • 达到均衡状态时,每个分片上的Chunk数量应为11652个(58260÷5=11652)。

  • 按照当前的迁移速度,任务完成还需要26.4天((11652-3719)÷300≈26.4)。

  • 该增加Shard节点任务进度为32%(3719÷11652=32%)。

说明

实际的业务场景中,总Chunk数量还会随着业务写入以及Chunk分裂而增加。因此假设条件为理想情况,实际耗时可能会更久。

步骤五:确认任务流是否阻塞(删除Shard节点)

如果删除Shard节点的任务被阻塞,执行sh.status()命令后,返回信息中没有看到过去时间段有Chunk数据迁移成功,且待删除Shard节点上依然残留部分Chunk数据未迁移走。此时删除Shard节点任务无法完成,并且会影响您在云数据库MongoDB控制台对实例的其他运维操作。

上述场景中sh.status()命令的返回信息示例如下图所示。

image.png

出现上述问题可能是因为Jumbo Chunk阻塞了删除Shard节点的进程,您可以通过如下的命令确认。

db.getSiblingDB("config").chunks.aggregate([{$match: {shard: "d-xxxxxxxxxxxxxx", jumbo:true}},{$group: {_id: "$ns", count: {$sum: 1}}}])

一般情况下,出现Jumbo Chunk是因为设计的分片键(Shard Key)不合理(例如存在热点Key)等因素导致。您可以在业务侧进行如下方法尝试解决:

  • 如果您的MongoDB数据库的大版本为4.4,则可以通过refineCollectionShardKey命令来优化您的Shard key设计,通过为原本分片键添加后缀的方式来使得分片键的基数更大,从而解决Jumbo Chunk的问题。

  • 如果您的MongoDB数据库的大版本为5.0及以上,则可以通过reshardCollection命令来将指定分片表按照新的分片键重新分片。更多介绍,请参见Reshard a Collection

  • 如果您确认部分业务数据可删除的话,则可以删除对应Jumbo Chunk中的数据,该方法可以减少Jumbo Chunk的大小。减少数据后Jumbo Chunk可能成为一个普通的Chunk,然后被均衡器Balancer迁出。

  • 您可以调大chunkSize参数来使判断Jumbo Chunk的条件发生变化,建议在阿里云技术支持工程师的协助下进行操作。

如果以上方法都无法解决您的问题,建议您提交工单联系技术支持协助解决。

加速新增或删除任务进程

如果您希望增加Shard节点或删除Shard节点的整体流程可以在更短的时间内完成,您可以尝试以下加速方法:

  • Balancer的窗口期时间调大,Chunk迁移过程带来的额外负载可能会对业务侧产生影响,请您评估风险后再进行调整。调整Balancer窗口期的方法,请参见管理MongoDB均衡器Balancer

  • 在业务低峰期手动执行moveChunk操作,更多介绍,请参见sh.moveChunk()。示例如下。

    sh.moveChunk("<db>.<collection>", {"<shard_key>": <value>}, "d-xxxxxxxxxxxxx")
    // example:
    sh.moveChunk("records.people", { zipcode: "53187" }, "shard0019")
  • 提交工单联系技术支持对相关内核参数进行调整。

相关文档