因数据插入、更新等操作长时间累积或者删除数据会导致产生磁盘碎片,本文介绍如何使用compact
命令回收磁盘碎片,从而提升磁盘利用率。该命令适用于回收Primary和Secondary节点的磁盘碎片。
建议使用空间分析功能回收磁盘碎片,控制台操作更简单且业务影响较小。空间分析仅回收Hidden节点的碎片,如需回收Primary节点和Secondary节点的碎片,需先进行主备切换。
目前仅以下版本支持使用空间分析功能回收磁盘碎片:
MongoDB 8.0:所有小版本。
MongoDB 7.0:所有小版本。
MongoDB 6.0:所有小版本。
MongoDB 5.0:所有小版本。
MongoDB 4.4:大于等于5.0.7版本。
MongoDB 4.2:大于等于4.0.23版本。
前提条件
实例的存储引擎为WiredTiger。
注意事项
数据备份:回收磁盘碎片前,建议对数据库数据备份。
compact
命令影响:compact
是进行空间整理与回收的命令,可以进行磁盘碎片回收。读写阻塞与性能影响
MongoDB 4.4之前的版本执行
compact
命令会导致集合所属的数据库被锁定,且该数据库的读写操作将被阻塞。碎片过多时,compact
命令的执行时间会比较长,此时可能会出现Hidden节点复制延迟大的风险。建议您在业务低峰期操作,并根据业务写入情况适当调大Oplog大小,或者升级数据库大版本至MongoDB 4.4及之后再进行碎片回收操作。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实例,执行
compact
命令的节点将维持在SECONDARY状态,不会触发重搭操作。
compact
命令无效:以下场景可能会导致
compact
命令执行无效,更多介绍请参见开源代码。物理集合大小小于1 MB。
文件前80%的存储空间中,空闲存储空间小于20%;文件前90%的存储空间中,空闲存储空间小于10%。
回收时间:执行
compact
命令回收磁盘碎片所需的时间与集合数据量、系统负载等因素有关。其他:
执行
compact
命令时,可能会出现释放的存储空间小于空闲的存储空间的情况。如果出现上述情况,请确保前一个compact
命令执行完毕后再发起下一个,避免频繁重复执行。compact
命令可以在磁盘空间满导致实例锁定的情况下执行。
背景信息
为什么会产生磁盘碎片?
形成:在删除云数据库MongoDB实例的数据后,这些被删除数据使用的存储空间会被标记为空闲,随后写入的新数据可能会被直接存储到这部分空闲的存储空间中,也可能会先扩展文件的存储空间再存储到文件末尾。上述情况将导致一部分空闲的存储空间不会被使用,这些未被使用的空闲存储空间被称之为磁盘碎片。
影响:磁盘碎片越多,磁盘的有效利用率就越低。比如磁盘大小100GB,碎片空间占据20GB,业务数据占据60GB,那么数据库实例的磁盘利用率为80%,但磁盘的有效利用率为60%。
什么时候需要回收磁盘碎片?
一次性删除大量数据
在删除大量数据后,这些文档占据的磁盘空间并不会被主动归还给操作系统,而是优先用于承载后续可能的写入,这可能导致磁盘上存在大量未被有效利用的碎片空间。
重要手动删除数据与自动过期(TTL)机制,均不会自动触发磁盘碎片回收,需要您手动回收磁盘碎片。
长时间运行高写入工作负载
如果您的实例长时间运行高写入工作负载(例如,频繁执行数据插入、更新、删除等操作),随着时间推移,磁盘上零散分布的碎片化空间将不断增加,从而累积产生大量磁盘碎片。
磁盘空间紧张且确认有>20%的碎片空间
当您的数据库实例的磁盘空间变得紧张时(例如,磁盘利用率达到85%-90%以上),回收磁盘碎片可以释放被碎片占用的存储空间,从而降低磁盘利用率并缓解磁盘空间压力。
查看磁盘存储空间
查看指定集合存储空间状况
您可以执行db.runCommand({collStats: <collection_name>})
命令,查看指定集合的存储空间状况。部分关键字介绍如下:
size
:表示集合的逻辑存储大小。storageSize
:表示集合的物理存储大小。freeStorageSize
:表示磁盘碎片中空闲并可以被回收的磁盘空间,云数据库MongoDB 4.4及以上版本支持查看。
在执行remove
命令删除文档后,size
的值会减少,但是storageSize
的值不一定会减少。可观察freeStorageSize
在storageSize
中的占比,占比率较高时,代表磁盘碎片率较高。
关于size
、storageSize
和freeStorageSize
关键字的更多信息,请参见collStats-Output。
预估回收的磁盘碎片空间
通过MongoDB Shell连接云数据库MongoDB实例。关于副本集实例,为降低业务影响,建议您连接副本集实例的Secondary节点操作。不同类型实例的连接方法如下:
将数据库切换至集合所在的数据库。
语法:
use <database_name>
参数说明:
<database_name>
为集合所在的数据库名称。说明您可以通过
show dbs
命令查询现有的数据库。示例:
切换至test_database数据库。
use test_database
查看集合需回收的磁盘碎片空间。
语法:
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节点)的磁盘碎片,执行的回收命令与回收主从节点磁盘碎片的命令相同。
通过Mongo Shell连接单节点或副本集实例。不同类型实例的连接方法如下:
将数据库切换至集合所在的数据库。
语法:
use <database_name>
参数说明:
<database_name>
为集合所在的数据库名称。说明您可以通过
show dbs
命令查询现有的数据库。示例:
切换至replica_database数据库。
use replica_database
查看回收磁盘碎片前数据库占用的磁盘空间。
db.stats()
说明该命令可以直接复制执行,无需修改。
回收集合的磁盘碎片。
语法:
db.runCommand({compact:"<collection_name>",force:true})
参数说明:
<collection_name>
:集合名称。说明您可以通过
show tables
命令查询现有的集合。force
:可选项,取值固定为true。如果您在4.2及以下版本的云数据库MongoDB实例主节点(Primary节点)上执行该命令,该参数为必填项。
示例:
db.runCommand({compact:"sharded_collection"})
执行成功的返回结果如下:
{ "ok" : 1 }
查看回收磁盘碎片后数据库占用的磁盘空间。
db.stats()
说明该命令可以直接复制执行,无需修改。
分片集群实例
分片集群实例只需要回收Shard组件中对应节点的磁盘碎片。Mongos组件和ConfigServer组件均不存储用户数据,并且增加和更新操作偏多,删除操作偏少,因此不需要回收磁盘碎片。
分片集群实例的只读节点不支持compact
命令,所以无法回收只读节点(ReadOnly节点)的磁盘碎片。
通过Mongo Shell连接分片集群实例,如何连接,请参见通过Mongo Shell连接MongoDB分片集群实例。
将数据库切换至集合所在的数据库。
语法:
use <database_name>
参数说明:
<database_name>
为集合所在的数据库名称。说明您可以通过
show dbs
命令查询现有的数据库。示例:
切换至sharded_database数据库。
use sharded_database
查看回收磁盘碎片前数据库占用的磁盘空间。
db.stats()
说明该命令可以直接复制执行,无需修改。
回收集合的磁盘碎片。
Shard组件中主节点(Primary节点)和从节点(Secondary节点)均需进行回收操作。
重要为降低业务影响,建议您优先回收Secondary节点的磁盘碎片,然后进行主备切换,将Primary节点切换到Secondary节点,再回收新Secondary节点的磁盘碎片。主备切换的具体操作,请参考分片集群实例设置主备切换。
回收Shard组件中从节点(Secondary节点)的磁盘碎片。
本操作在mongo shell和mongosh中执行存在一定差异,请根据您使用的客户端来选择对应的操作方法。
说明相比较mongosh 1.x版本,2.x版本新增了支持设置读写偏好参数。具体说明,请参见Read Preference。
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 1.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"}})
mongosh 2.x
语法:
db.runCommand({runCommandOnShard:"<Shard ID>","command":{compact:"<collection_name>"}},{readPreference: "secondary"})
参数说明:
<Shard ID>
:Shard组件的ID。说明您可以登录MongoDB管理控制台,在目标实例基本信息页面的Shard列表区域查看Shard组件的ID。
<collection_name>
:集合名称。说明您可以通过
show tables
命令查询现有的集合。
示例:
db.runCommand({runCommandOnShard:"d-2ze657bce53fb6d4","command":{compact:"test_collection"}}, { readPreference: "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}})
查看回收磁盘碎片后数据库占用的磁盘空间。
db.stats()
说明该命令可以直接复制执行,无需修改。
常见问题
Q:执行命令报错,“Compaction interrupted on table:xxx due to cache eviction pressure' on server xxx. ”。
A:低版本的小规格实例在执行compact
的过程中,可能因缓存压力而中途退出,建议在业务低峰期操作。