通过 delete 或 TTL 索引删除数据后,物理磁盘空间不会自动释放,未被复用的空闲空间即磁盘碎片。本文介绍三种回收方式:自动回收计划、控制台手动回收和命令行 compact。
选择回收方式
在云数据库 MongoDB 版中,通过 delete 或 TTL 索引删除数据后,物理磁盘空间不会自动释放。被删除数据的空间会标记为空闲、留待后续写入复用,未被复用的部分即磁盘碎片。碎片累积导致磁盘使用率偏高时,需回收碎片释放物理空间。
三种回收方式对比如下:
对比维度 | 方式一:自动回收计划 | 方式二:控制台手动回收 | 方式三:命令行 compact |
作用节点 | 仅 Hidden 节点 | 仅 Hidden 节点 | Primary节点或 Secondary节点 |
内核版本要求 |
|
| 无 |
每轮可回收空间上限 | 100 GB | 无 | 无 |
业务影响 | 无(可维护时段执行) | 无(作用于 Hidden) | 有,具体请参见compact 对业务的影响 |
适用场景 | 适用于数量较多的可回收空间较小的集合 | 适用于可回收空间较大的集合 | 量大、精确控制、控制台回收不动 |
drop 整个集合或数据库会立即释放其占用的物理空间,但这属于删除数据操作,仅建议在磁盘紧张或实例锁定时应急使用,不作为常规碎片回收手段。
方式一:设置自动回收计划
云数据库 MongoDB 版提供碎片回收计划,由 DAS(数据库自治服务)在实例可维护时段自动检测并对 Hidden 节点执行 compact,无需人工干预,不影响业务。
触发条件与规则
系统只对同时满足以下条件的集合自动回收:
集合的(索引空间 + 数据空间)之和大于 1 GB。
碎片率大于 20%。
其他规则:
单轮回收总量上限 100 GB,超出部分在后续轮次继续回收。
上述阈值为系统内置,用户不可自定义。
配置入口
登录MongoDB 管理控制台,进入目标实例。
在左侧导航栏选择 CloudDBA > 空间分析。
在数据空间列表中,找到碎片率列的回收链接并单击。
在弹出的碎片回收计划对话框中,单击新增计划并确认。
方式二:控制台手动回收
当集合的可回收空间超过 100 GB时,可使用手动回收。
集合的可回收空间超过100 GB时,回收时间可能超过1小时,请合理安排回收时间。
操作步骤
进入目标实例,在左侧导航栏选择 CloudDBA > 空间分析。
在数据空间列表中查看各集合的碎片率与可回收空间。
对需要回收的高碎片率集合,单击对应的回收执行。
确认回收成功
回收完成后,重新执行空间分析,查看目标集合碎片率是否下降。请确认查看的节点与实际回收的节点一致(参见常见问题与排障)。
如果用命令行在主节点或从节点执行了 compact,控制台空间分析中的碎片率不会变化,这不代表回收未生效。请到监控信息页面切换到实际操作的节点确认效果。
方式三:命令行 compact
适用于单集合超过 100 GB、控制台回收无法满足的场景。
权限要求
执行 compact 需要账号有dbAdmin权限或者hostManager(版本>8.0)权限。否则执行会报权限错误:
not authorized on <database> to execute command { 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状态,不会触发重搭操作。
无论哪个版本,都建议优先在 Hidden 或 Secondary 节点回收,避免影响主库。回收磁盘碎片前,建议对数据库进行数据备份。
compact 执行耗时
耗时与集合数据量、系统负载等因素相关,无法精确预估。集合越大、碎片越多耗时越长,超大集合(数百 GB)可能显著更久。建议先用 dryRun: true 预估可释放空间,并在业务低峰执行。
以下数据仅供量级参考:
实测参考(MongoDB 4.4):对约 2.5 GB、50% 碎片的集合执行 compact,耗时约 5 秒、返还约 1.9 GB 空间。
compact 命令无效的场景
以下场景可能导致 compact 命令执行无效:
物理集合大小小于 1 MB。
碎片率小于 20%。
文件前 80% 的存储空间中,空闲存储空间小于 20%;文件前 90% 的存储空间中,空闲存储空间小于 10%。
更多介绍请参见 block_compact。
副本集实例
不建议直连主节点(Primary)执行 compact。compact 会占用较多 IO/CPU,在主节点执行会影响线上业务。建议对 Secondary 节点执行,再通过主备切换轮流回收各节点。
单节点(StandAlone)实例只有一个节点,直接连接该主节点执行 compact 即可。
在 Secondary 节点上连接后执行以下命令:
use <database>
// 回收前:查看数据库占用空间,便于回收后对比
db.stats()
// 预演:只估算可释放空间,不实际回收
db.runCommand({ compact: "<collection>", dryRun: true })
// 实际回收
db.runCommand({ compact: "<collection>" })
// 回收后:再次查看,对比空间是否下降
db.stats()其中 dryRun: true 为预演模式,仅返回 estimatedBytesFreed(预估可释放字节数),不实际修改文件。确认预演结果后再去掉该参数执行实际回收。回收前后分别执行 db.stats() 可对比数据库占用空间的变化,确认回收效果。
请确保上一次 compact 执行完成后,再对同一集合开始新的 compact。
分片集群实例
分片集群只需回收 Shard 组件中对应节点的磁盘碎片。Mongos 和 ConfigServer 组件不存储用户数据,无需回收。
分片集群实例的只读节点不支持compact命令,所以无法回收只读节点(ReadOnly节点)的磁盘碎片。回收 Secondary 节点碎片:执行 runCommandOnShard 时需将读取偏好设置为 Secondary 节点,不同客户端语法不同。请根据您使用的客户端选择对应的操作方法:
mongosh 2.x
mongosh 2.x 支持在 runCommand 的第二个参数中直接指定读取偏好:
db.runCommand({runCommandOnShard:"<Shard ID>","command":{compact:"<collection_name>"}},{readPreference: "secondary"})mongosh 1.x
mongosh 1.x 需先通过 setReadPref 设置读取偏好,再执行命令:
db.getMongo().setReadPref('secondary')
db.runCommand({runCommandOnShard:"<Shard ID>","command":{compact:"<collection_name>"}})Tab 2 正文
mongo shell(旧版)
mongo shell(旧版)需在命令中添加 $queryOptions 指定读取偏好:
db.runCommand({runCommandOnShard:"<Shard ID>","command":{compact:"<collection_name>"},$queryOptions: {$readPreference: {mode: 'secondary'}}})回收 Primary 节点碎片(不推荐):
为降低业务影响,建议您进行主备切换,将Primary节点切换到Secondary节点,再回收新Secondary节点的磁盘碎片。主备切换的具体操作,请参考分片集群实例设置主备切换。
db.adminCommand({
runCommandOnShard: "<shardId>",
dbName: "<database>",
command: { compact: "<collection>", force: true }
})
常见问题与排障
执行了回收但看不到效果
请按以下顺序自查:
操作的节点与查看结果的节点是否一致(最常见原因):控制台空间分析的回收作用于 Hidden 节点,页面碎片率也默认显示 Hidden 节点。命令行 compact 作用于您连接的节点。如果在主节点或从节点手动执行 compact,却在控制台空间分析查看碎片率,数字不会变化。正确做法:到监控信息页面,切换到实际操作的节点查看磁盘空间使用率。
是否为文件内部碎片:compact 只能截断文件尾部的连续空闲空间,文件中间的可重用空间无法回收。若碎片主要分布在文件内部,compact 后空间可能几乎不变,这是 WiredTiger 存储引擎的机制,并非故障。可通过
db.<collection>.stats()查看freeStorageSize占storageSize的比例来判断可回收空间大小。是否未达回收条件:碎片率小于 20%、集合物理大小小于 1 MB、或都是小表时,可回收的绝对空间很小,可能无法回收。
是否直连主节点被拦截:若报
use force:true to force,这是保护机制,说明在主节点执行了 compact。不建议强制,请改在 Hidden 或 Secondary 节点执行,或使用控制台空间分析回收 Hidden 节点。
碎片率为什么回收不到 0%
正常现象。compact 只回收文件尾部的连续空闲空间,文件内部的可重用空间会保留给后续写入复用,因此碎片率通常无法降到 0%。这是 WiredTiger 存储引擎的存储策略。
报 Interrupted ... cache eviction pressure
报错原因:compact 执行期间会给 WiredTiger 存储引擎的缓存带来驱逐压力(cache eviction pressure)。低版本、小规格实例内存资源有限,当缓存压力过大、无法及时驱逐时,compact 会被中断并提前退出。
处理办法:可在业务低峰期重试、触发一次主备切换后再试,或升级实例规格后再回收。
磁盘满导致实例锁定时能否执行 compact
可以。 磁盘写满后实例进入锁定状态,此时写入(insert)和删除(delete)操作会被拒绝并报错cloud instance error, disk locked...,但find、compact 和 drop 操作仍可执行。
为何 delete 操作也被拒绝?
因为 delete 操作本身需写入 oplog、仍会消耗磁盘,在锁定状态下同样被拒绝;而 drop 和 compact 属于元数据级/空间整理操作,被系统放行。锁定态下可执行 compact 回收碎片释放空间,也可执行 drop 直接删除集合释放空间,两者都是无需扩容的恢复路径。
最快恢复决策
你的情况 | 推荐做法 | 恢复速度 | 代价 |
有可安全删除的无用集合/库。 | 执行 | 释放空间后约 4~5 分钟自动解锁(有检测延迟,非实时)。 | 无需付费,但会删除数据,需确认可删除。 |
数据不能删,但有较多碎片可回收。 | 执行 compact(锁定状态下 compact 被放行)。 | compact 释放空间后约 5 分钟自动解锁(有检测延迟,非实时)。 |
|
没有可删数据 / 数据不能丢。 | 扩容完成后解锁。 |
|
解锁后请及时回收碎片或清理无用数据,避免磁盘再次打满。详见解决因磁盘空间耗尽导致的锁定。
控制台使用率与告警对不上(如控制台 60% 但告警 90%)
这是展示维度不同造成的,不是数据错误:
告警按"单个节点的最高值"触发:副本集里 Primary、Secondary、Hidden 各节点的磁盘使用率可能不同(Secondary/Hidden 常因 oplog、回收时机、临时文件等原因高于 Primary)。只要任一节点超过阈值就会告警。
控制台默认展示的不是"最高节点":实例基本信息页展示聚合值,空间分析默认展示 Hidden 节点,都可能低于触发告警的那个节点。
如何看各节点真实使用率:进入实例监控信息页,将查看模式切换为子节点独立,即可分别查看 Primary / Secondary 各节点的磁盘空间使用率,找到真正偏高、触发告警的节点。
主从节点磁盘使用率为何不一致、如何进一步处置,详见MongoDB 实例空间使用率高问题。
能不能缩小磁盘省钱
不支持缩容。云数据库 MongoDB 版所有版本均不支持缩减已购买的磁盘空间。即使已清理大量数据、回收碎片,也只能保持或扩大磁盘规格,无法直接调小。
如确需降低磁盘规格,只能新建一个更小规格的实例,再通过 DTS 将数据迁移过去,然后释放原实例。请评估迁移成本与停机窗口后再操作。
变配与规格限制详见变更副本集实例配置。
附录
为什么会产生磁盘碎片
通过 delete 或 TTL 过期删除的数据,只是被标记删除,其占用的空间不会立即归还操作系统,而是保留为空闲块供后续写入复用。当删除多、写入少时,这些空闲块长期不被复用,就形成碎片,表现为数据量下降但磁盘使用率不降。
何时需要回收磁盘碎片
出现以下情况时,建议回收磁盘碎片:
删除大量数据后:删除大量文档后,释放的空间不会归还操作系统,而是保留供后续写入复用,磁盘上会留下大量碎片空间。
重要手动删除(
delete)与 TTL 过期删除都不会自动回收磁盘碎片,需手动回收。长期高写入负载后:持续的高写入负载(频繁 insert、update、delete)会在磁盘上逐渐累积碎片空间。
磁盘紧张且碎片率超过 20% 时:当磁盘使用率达到 85%~90% 或更高时,回收碎片可释放空间、缓解存储压力。
查看与预估可回收空间
连接实例后(副本集实例建议连接 Secondary 节点以减少对业务的影响),执行 db.runCommand({collStats: "<collection>"}) 查看集合的存储状况,关注以下字段:
size:集合的逻辑存储大小。storageSize:集合的物理存储大小。freeStorageSize:集合内可回收的空闲空间(MongoDB 4.4 及以上版本支持)。
使用 remove 删除文档后,size 会下降,但 storageSize 可能不变。freeStorageSize 在 storageSize 中的占比越高,代表碎片率越高。
也可执行以下命令预估某集合可回收的碎片空间(返回可重用字节数):
db.<collection>.stats().wiredTiger["block-manager"]["file bytes available for reuse"]对几乎为空的实例,freeStorageSize 可能返回空值,此时可结合 wiredTiger["block-manager"]["file bytes available for reuse"](可重用字节)与 ["file size in bytes"](文件总大小)来估算碎片占比。字段说明详见 collStats Output。