回收磁盘碎片以提升磁盘利用率

因数据插入、更新等操作长时间累积或者删除数据会导致产生磁盘碎片,本文介绍如何使用compact命令回收磁盘碎片,从而提升磁盘利用率。该命令适用于回收PrimarySecondary节点的磁盘碎片。

建议使用空间分析功能回收磁盘碎片,控制台操作更简单且业务影响较小。空间分析仅回收Hidden节点的碎片。回收Primary节点和Secondary节点的碎片,需先进行主备切换。具体操作,请参见空间分析

前提条件

实例的存储引擎为WiredTiger。

背景信息

  • 在删除云数据库MongoDB实例的数据后,这些被删除数据使用的存储空间会被标记为空闲,随后写入的新数据可能会被直接存储到这部分空闲的存储空间中,也可能会先扩展文件的存储空间再存储到文件末尾。上述情况将导致一部分空闲的存储空间不会被使用,这些未被使用的空闲存储空间被称之为磁盘碎片,磁盘碎片越多,磁盘利用率就越低。

  • 您可以执行db.runCommand({collStats: <collection_name>}) 命令访问节点,查看指定集合的存储空间状况。部分关键字介绍如下:

    • size:表示集合的逻辑存储大小。

    • storageSize:表示集合的物理存储大小。

    • freeStorageSize:表示磁盘碎片中空闲并可以被回收的磁盘空间,云数据库MongoDB 4.4及以上版本支持查看。

    在执行remove命令删除文档后,size的值会减少,但是storageSize的值不一定会减少。可观察freeStorageSizestorageSize中的占比,占比率较高时,代表磁盘碎片率较高。

说明

关于sizestorageSizefreeStorageSize关键字的更多信息,请参见collStats-Output

  • compact在云数据库MongoDB中是一个进行空间整理与回收的命令。关于compact命令的更多信息,请参见compact

注意事项

  • 数据备份:回收磁盘碎片前,建议对数据库数据进行备份,备份方法,请参见手动备份MongoDB数据

  • compact命令影响

    • 读写阻塞与性能影响

      • MongoDB 4.4之前的版本执行compact命令会导致集合所属的数据库被锁定,且该数据库的读写操作将被阻塞。建议您在业务低峰期操作,或者将版本升级至MongoDB 4.4及之后再进行碎片回收操作。关于版本升级,请参见升级数据库大版本。受阻塞的具体介绍,请参见MongoDB官网文档

      • MongoDB 4.4及之后的版本compact命令不再阻塞业务读写,但执行过程中可能会影响性能,建议您在业务低峰期操作。

    • 节点重搭

      • MongoDB 3.4全版本、MongoDB 4.0全版本、MongoDB 4.2的早期小版本(小于等于4.0.22版本)、MongoDB 4.4的早期小版本(小于等于5.0.6版本)实例,正在执行compact命令的节点会进入RECOVERING状态,如果持续时间过长,该节点会被实例探活组件认定为节点不健康从而触发相应的重搭操作。详细信息,请参见MongoDB官方文档。关于MongoDB版本的更多信息,请参见MongoDB小版本说明

      • 上述版本之后的MongoDB实例,执行compact命令的节点则会维持在SECONDARY状态,不会触发重搭操作。

  • compact命令无效:

    以下场景可能会导致compact命令执行无效,更多介绍请参见开源代码

    • 物理集合大小小于1 MB。

    • 文件前80%的存储空间中,空闲存储空间小于20%;文件前90%的存储空间中,空闲存储空间小于10%。

  • 回收时间:执行compact命令回收磁盘碎片所需的时间与集合数据量、系统负载等因素有关。

  • 其他

    • 执行compact命令时,可能会出现释放的存储空间小于空闲的存储空间的情况。如果出现上述情况,请确保前一个compact命令执行完毕后再发起下一个,避免频繁重复执行。

    • compact命令可以在磁盘空间满导致实例锁定的情况下执行。

预估回收的磁盘碎片空间

  1. 通过MongoDB Shell连接云数据库MongoDB实例。关于副本集实例,为降低业务影响,建议您连接副本集实例的Secondary节点操作。不同类型实例的连接方法如下:

  2. 将数据库切换至集合所在的数据库。

    语法:

    use <database_name>

    参数说明:<database_name>为集合所在的数据库名称。

    说明

    您可以通过show dbs命令查询现有的数据库。

    示例:

    切换至test_database数据库。

    use test_database
  3. 查看集合需回收的磁盘碎片空间。

    语法:

    db.<collection_name>.stats().wiredTiger["block-manager"]["file bytes available for reuse"]

    参数说明:<collection_name>为集合名称。

    说明

    您可以通过show tables命令查询现有的集合。

    示例:

    db.test_database_collection.stats().wiredTiger["block-manager"]["file bytes available for reuse"]

    返回结果如下:

    207806464

    该返回结果表示预估回收的磁盘碎片空间为207806464 Byte。

回收单节点或副本集实例的磁盘碎片

  • 单节点实例只有一个StandAlone节点,您只需要连接主节点(Primary节点),执行compact命令回收主节点(Primary节点)的磁盘碎片。

  • 副本集实例具有多个节点,主节点(Primary节点)和从节点(Secondary节点)均需进行回收操作。为降低业务影响,建议您优先回收Secondary节点的磁盘碎片,然后进行主备切换,将Primary节点切换到Secondary节点,再回收新Secondary节点的磁盘碎片。主备切换的具体操作,请参考副本集实例设置主备切换

    说明

    如果副本集实例具有只读节点(ReadOnly节点),您还需要回收只读节点(ReadOnly节点)的磁盘碎片,执行的回收命令与回收主从节点磁盘碎片的命令相同。

  1. 通过Mongo Shell连接单节点或副本集实例。不同类型实例的连接方法如下:

  2. 将数据库切换至集合所在的数据库。

    语法:

    use <database_name>

    参数说明:<database_name>为集合所在的数据库名称。

    说明

    您可以通过show dbs命令查询现有的数据库。

    示例:

    切换至replica_database数据库。

    use replica_database
  3. 查看回收磁盘碎片前数据库占用的磁盘空间。

    db.stats()
    说明

    该命令可以直接复制执行,无需修改。

  4. 回收集合的磁盘碎片。

    语法:

    db.runCommand({compact:"<collection_name>",force:true})

    参数说明:

    • <collection_name>:集合名称。

      说明

      您可以通过show tables命令查询现有的集合。

    • force:可选项,取值固定为true。

      如果您在4.2及以下版本的云数据库MongoDB实例主节点(Primary节点)上执行该命令,该参数为必填项。

    示例:

    db.runCommand({compact:"sharded_collection"})

    执行成功的返回结果如下:

    { "ok" : 1 }
  5. 查看回收磁盘碎片后数据库占用的磁盘空间。

    db.stats()
    说明

    该命令可以直接复制执行,无需修改。

回收分片集群实例的磁盘碎片

分片集群实例只需要回收Shard组件中对应节点的磁盘碎片。Mongos组件和ConfigServer组件均不存储用户数据,并且增加和更新操作偏多,删除操作偏少,因此不需要回收磁盘碎片。

说明

分片集群实例的只读节点不支持compact命令,所以无法回收只读节点(ReadOnly节点)的磁盘碎片。

  1. 通过Mongo Shell连接分片集群实例,如何连接,请参见通过Mongo Shell连接MongoDB分片集群实例

  2. 将数据库切换至集合所在的数据库。

    语法:

    use <database_name>

    参数说明:<database_name>为集合所在的数据库名称。

    说明

    您可以通过show dbs命令查询现有的数据库。

    示例:

    切换至sharded_database数据库。

    use sharded_database
  3. 查看回收磁盘碎片前数据库占用的磁盘空间。

    db.stats()
    说明

    该命令可以直接复制执行,无需修改。

  4. 回收集合的磁盘碎片。

    Shard组件中主节点(Primary节点)和从节点(Secondary节点)均需进行回收操作。为降低业务影响,建议您优先回收Secondary节点的磁盘碎片,然后进行主备切换,将Primary节点切换到Secondary节点,再回收新Secondary节点的磁盘碎片。主备切换的具体操作,请参考分片集群实例设置主备切换

    • 回收Shard组件中主节点(Primary节点)的磁盘碎片。

      语法:

      db.runCommand({runCommandOnShard:"<Shard ID>","command":{compact:"<collection_name>",force:true}})

      参数说明:

      • <Shard ID>:Shard组件的ID。

        说明

        您可以登录MongoDB管理控制台,在目标实例基本信息页面的Shard列表区域查看Shard组件的ID。

      • <collection_name>:集合名称。

        说明

        您可以通过show tables命令查询现有的集合。

      • force:可选项,取值固定为true。

        如果您的分片集群实例版本为4.2及以下版本,该参数为必填项。

      示例:

      db.runCommand({runCommandOnShard:"shard01","command":{compact:"sharded_collection",force:true}})
    • 回收Shard组件中从节点(Secondary节点)的磁盘碎片。

      本操作在mongo shellmongosh中执行存在一定差异,请根据您使用的客户端来选择对应的操作方法。

      mongo shell

      语法:

      db.runCommand({runCommandOnShard:"<Shard ID>","command":{compact:"<collection_name>"},$queryOptions: {$readPreference: {mode: 'secondary'}}})

      参数说明:

      • <Shard ID>:Shard组件的ID。

        说明

        您可以登录MongoDB管理控制台,在目标实例基本信息页面的Shard列表区域查看Shard组件的ID。

      • <collection_name>:集合名称。

        说明

        您可以通过show tables命令查询现有的集合。

      示例:

      db.runCommand({runCommandOnShard:"shard01","command":{compact:"sharded_collection"},$queryOptions: {$readPreference: {mode: 'secondary'}}})

      mongosh

      说明

      由于mongosh v2.x版本暂不支持执行runCommandOnShard命令,在执行本操作时请您使用mongosh v1.x版本。

      语法:

      db.getMongo().setReadPref('secondary')
      db.runCommand({runCommandOnShard:"<Shard ID>","command":{compact:"<collection_name>"}})

      参数说明:

      • <Shard ID>:Shard组件的ID。

        说明

        您可以登录MongoDB管理控制台,在目标实例基本信息页面的Shard列表区域查看Shard组件的ID。

      • <collection_name>:集合名称。

        说明

        您可以通过show tables命令查询现有的集合。

      示例:

      db.getMongo().setReadPref('secondary')
      db.runCommand({runCommandOnShard:"d-2ze91ae9d55d6604","command":{compact:"test"}})
  5. 查看回收磁盘碎片后数据库占用的磁盘空间。

    db.stats()
    说明

    该命令可以直接复制执行,无需修改。

相关问题

解决因磁盘空间耗尽导致的锁定或无法写入问题