如何清理孤立文档

如果MongoDB分片集群版实例中存在孤立文档,您在进行数据迁移或释放Shard节点任务时,可能会出现影响实例性能或产生报错等问题。因此,您在进行上述操作前需要先检查实例内是否存在孤立文档并对孤立文档进行清理。

前提条件

  • 服务器已安装Mongo Shell,并且可以成功连接MongoDB分片集群实例。如何连接实例,请参见通过Mongo Shell连接MongoDB分片集群实例

    说明

    本文中操作示例均在Mongo Shell客户端上执行,其他连接工具的执行命令和返回信息可能与本文不一致。

  • MongoDB实例大版本为4.2及以上且小版本为4.0.6及以上。实例低于上述版本的话,在执行清理孤立文档操作时会出现报错。如何升级小版本和大版本,请参见升级数据库大版本升级数据库小版本

检查孤立文档

云数据库 MongoDB 版与自建MongoDB检查孤立文档的方法一致,具体方法如下。

MongoDB 5.0及以前版本

使用root或高权限账号连接到Mongos节点,执行以下命令。

db.getSiblingDB("<db>").<collection>.find().readPref("secondary").readConcern("local").explain("executionStats")

参数说明。

参数

说明

<db>

MongoDB数据库名称。

<collection>

集合名称。

重要
  • 由于该命令会扫描所有Shard节点上的文档,为降低对业务的影响,该命令设置了readPreference参数,将会在Secondary节点上执行。

  • 该命令的执行耗时与库表的数据量和文档数成正比,当数据量较大时,查询耗时会比较长,请耐心等待。

  • 您需要对实例中每个库表均执行一次该命令。

返回示例如下。

image.png

返回结果中,您仅需要关注SHARDING_FILTER阶段,其中chunkSkips的值就是该库表在当前Shard节点上的孤立文档数量。

MongoDB 6.0及以后版本

说明

本操作将通过$shardedDataDistribution聚合阶段来实现,该功能仅7.0.1(基准版本为6.0.3)及以上内核小版本支持。如果您的内核小版本低于7.0.1,建议您升级至最新版,升级方法,请参见升级数据库小版本

如果您不方便升级至最新版,可以参考MongoDB 5.0及以前版本检查孤立文档的方法。

使用root或高权限账号连接到Mongos节点,执行以下命令。

db.getSiblingDB("admin").aggregate( [{ $shardedDataDistribution: { } }] ).pretty()
//如果只需要确认某个单独的namespace是否包含孤立文档,则按需添加过滤字段即可
db.getSiblingDB("admin").aggregate( [{ $shardedDataDistribution: { } },{ $match: { "ns": "<db>.<collection>" } }] ).pretty()

返回结果中会展示分片集群中所有分片集合的数据分布情况(包括孤立文档)。返回示例如下。

[
  {
    "ns": "test.names",
    "shards": [
      {
        "shardName": "shard-1",
        "numOrphanedDocs": 0,
        "numOwnedDocuments": 6,
        "ownedSizeBytes": 366,
        "orphanedSizeBytes": 0
      },
      {
        "shardName": "shard-2",
        "numOrphanedDocs": 0,
        "numOwnedDocuments": 6,
        "ownedSizeBytes": 366,
        "orphanedSizeBytes": 0
      }
    ]
  }
]

返回结果中的numOrphanedDocs参数值即是某个库表在该分片上的孤立文档数量,当该值不为0时,则需要对孤立文档进行清理。更多说明,请参见MongoDB官方文档

清理孤立文档

说明

MongoDB 4.2以下大版本或4.0.6以下小版本的实例在执行清理脚本时会报错。如何查看实例当前版本,请参见MongoDB小版本说明。如何升级小版本和大版本,请参见升级数据库大版本升级数据库小版本

云数据库 MongoDB 版与自建MongoDB清理孤立文档的方法存在差异,具体方法如下。

云数据库 MongoDB 版

  1. 在可以连接分片集群实例的服务器上,创建一个用于清理孤立文档的JS脚本,脚本名称为cleanupOrphaned.js

    MongoDB 4.4及以后版本和MongoDB 4.2及以前版本清理孤立文档的脚本略有区别,具体内容如下。

    MongoDB 4.4及以后版本

    说明

    该脚本用于清理多个Shard节点的多个数据库中所有集合的孤立文档。如需清理特定集合中的孤立文档,可自行修改JS脚本来完成。

    // shard实例名称列表
    var shardNames = ["shardName1", "shardName2"];
    // 数据库列表
    var databasesToProcess = ["database1", "database2", "database3"];
    
    shardNames.forEach(function(shardName) {
        // 遍历指定的数据库列表
        databasesToProcess.forEach(function(dbName) {
            var dbInstance = db.getSiblingDB(dbName);
            // 获取该数据库实例的所有集合名称
            var collectionNames = dbInstance.getCollectionNames();
            
            // 遍历每个集合
            collectionNames.forEach(function(collectionName) {
                // 完整的集合名称
                var fullCollectionName = dbName + "." + collectionName;
                // 构建 cleanupOrphaned 命令
                var command = {
                    runCommandOnShard: shardName,
                    command: { cleanupOrphaned: fullCollectionName }
                };
    
                // 执行命令
                var result = db.adminCommand(command); 
                printjson(result);
                if (result.ok) {
                    print("Cleaned up orphaned documents for collection " + fullCollectionName + " on shard " + shardName);
                } else {
                    print("Failed to clean up orphaned documents for collection " + fullCollectionName + " on shard " + shardName);
                }
            });
        });
    });

    您需要对脚本中的shardNamesdatabasesToProcess参数的值进行修改,具体说明如下:

    • shardNames:分片集群实例中待清理孤立文档的Shard节点的ID数组,您可以在实例基本信息页面的Shard列表区域获取,例如d-bp15a3796d3a****

    • databasesToProcess:待清理孤立文档的数据库名称数组。

    MongoDB 4.2及以前版本

    说明

    该脚本用于清理多个Shard节点中指定数据库下指定集合的孤立文档。如需清理数据库中多个集合的孤立文档,您可以修改fullCollectionName参数并多次执行,也可以自行修改脚本通过遍历的方式执行。

    function cleanupOrphanedOnShard(shardName, fullCollectionName) {
        var nextKey = { };
        var result;
    
        while ( nextKey != null ) {
            var command = {
                runCommandOnShard: shardName,
                command: { cleanupOrphaned: fullCollectionName, startingFromKey: nextKey }
            };
    
            result = db.adminCommand(command);
            printjson(result);
    
            if (result.ok != 1 || !(result.results.hasOwnProperty(shardName)) || result.results[shardName].ok != 1 ) {
                print("Unable to complete at this time: failure or timeout.")
                break
            }
    
            nextKey = result.results[shardName].stoppedAtKey;
        }
    
        print("cleanupOrphaned done for coll: " + fullCollectionName + " on shard: " + shardName)
    }
    
    var shardNames = ["shardName1", "shardName2", "shardName3"]
    var fullCollectionName = "database.collection"
    
    shardNames.forEach(function(shardName) {
        cleanupOrphanedOnShard(shardName, fullCollectionName);
    });

    您需要对脚本中的shardNamesfullCollectionName参数的值进行修改,具体说明如下:

    • shardNames:分片集群实例中待清理孤立文档的Shard节点的ID数组,您可以在实例基本信息页面的Shard列表区域获取,例如d-bp15a3796d3a****

    • fullCollectionName:需要替换为待清理孤立文档的集合名称,格式为数据库名称.集合名称

  2. 运行JS脚本清理孤立文档。

    您可以直接通过Mongo Shell命令运行JS脚本,也可以登录数据库后再运行JS脚本,具体方法如下。

    通过Mongo Shell命令运行JS脚本

    cleanupOrphaned.js脚本所在的目录下,执行以下命令清理孤立文档。

    mongo --host <Mongoshost> --port <Primaryport>  --authenticationDatabase <database> -u <username> -p <password> cleanupOrphaned.js > output.txt

    参数说明如下。

    参数

    说明

    <Mongoshost>

    分片集群实例Mongos节点的连接地址,格式为s-bp14423a2a51****.mongodb.rds.aliyuncs.com

    <Primaryport>

    分片集群实例Mongos节点的端口号,默认为3717。

    <database>

    鉴权数据库名,即数据库账号所属的数据库。

    <username>

    数据库账号。

    <password>

    数据库账号的密码。

    output.txt

    执行结果保存到output文件中。

    登录数据库后运行JS脚本

    cleanupOrphaned.js脚本所在的目录下,通过Mongo Shell连接数据库。连接实例的方法,请参见通过Mongo Shell连接MongoDB分片集群实例

    登录成功后,执行以下命令运行脚本。

    load("cleanupOrphaned.js")

    load命令运行JS脚本

自建MongoDB

  1. 在可以连接自建MongoDB数据库的服务器上下载cleanupOrphaned.js脚本文件。

    wget "https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/120562/cn_zh/1564451237979/cleanupOrphaned.js"
  2. 修改cleanupOrphaned.js脚本文件,将test替换为待清理孤立文档的数据库名。

    重要

    如果您有多个数据库,您需要重复执行步骤2~步骤3。

  3. 执行如下命令,清理Shard节点中指定数据库下所有集合的孤立文档。

    说明

    您需要重复执行本步骤,为每个Shard节点清理孤立文档。

    mongo --host <Shardhost> --port <Primaryport>  --authenticationDatabase <database> -u <username> -p <password> cleanupOrphaned.js
    说明
    • <Shardhost>:Shard节点的IP地址。

    • <Primaryport>:Shard节点中的Primary节点的服务端口。

    • <database>:鉴权数据库名,即数据库账号所属的数据库。

    • <username>:登录数据库的账号。

    • <password>:登录数据库的密码。

    示例:

    本案例的自建MongoDB数据库有三个Shard节点,所以需要分别为这三个节点清除孤立文档。

    mongo --host 172.16.1.10 --port 27018  --authenticationDatabase admin -u dtstest -p 'Test123456' cleanupOrphaned.js
    mongo --host 172.16.1.11 --port 27021 --authenticationDatabase admin -u dtstest -p 'Test123456' cleanupOrphaned.js
    mongo --host 172.16.1.12 --port 27024  --authenticationDatabase admin -u dtstest -p 'Test123456' cleanupOrphaned.js

相关文档

清理孤立文档主要使用cleanupOrphaned命令,关于该命令的更多介绍请参见官网文档: