compact命令在分片集群中不可直接使用,必须直连各shard主节点单独执行;mongos不转发该命令,且需确认WiredTiger引擎、碎片率达标、磁盘预留空间充足。

compact 命令在分片集群上根本不能直接用
你执行 db.runCommand({compact:"mycol"}) 时如果连的是 mongos,会直接报错 "command not supported from mongos"。这是因为 compact 是存储层命令,只能作用于单个 mongod 实例,而分片集群里每个 shard 都是独立的 mongod(或副本集),mongos 不转发该命令。
常见错误现象:在 mongos 上执行后无响应、返回空结果、或提示未授权/不支持;实际磁盘空间毫无变化。
- 必须登录到每个 shard 的主节点(或任意可写节点)分别执行
- 若该 shard 是副本集,需在每个成员上单独运行(
compact不自动同步) - 对 config server 或 mongos 执行完全无效
- WiredTiger 引擎下能释放空间,MMAPv1 下仅整理碎片但不缩文件
执行 compact 前必须确认的三件事
盲目运行 compact 可能导致服务卡死、IO 扛不住、甚至因空间不足失败。它不是“一键瘦身”,而是重写整个集合数据文件+索引。
-
df -h检查数据目录所在磁盘剩余空间:WiredTiger 要求至少预留 2GB 临时空间;MMAPv1 更高(建议 ≥ 当前storageSize的 30%) - 查
db.mycol.stats().wiredTiger或db.mycol.stats().size / db.mycol.stats().storageSize:比值低于 0.7 才值得压缩;否则碎片占比小,收益极低 - 确认当前使用的是 WiredTiger 引擎:
db.serverStatus().storageEngine.name返回"wiredTiger";MMAPv1 已废弃,且compact对其几乎不释放磁盘
shard 节点上 compact 的正确操作链
不能只在 primary 上跑完就以为万事大吉。分片集群中每个 shard 是独立的数据单元,必须按 shard 逐个处理,且要考虑读写影响。
- 先通过
sh.status()查出目标 collection 分布在哪几个 shard 上(例如shard0001,shard0002) - 用
mongo --host <shard_host>:port -u ... -p ...</shard_host>直连对应 shard 的主节点(非 mongos) - 切换数据库:
use your_db_name,再执行:db.runCommand({compact: "your_collection", force: true})(force:true在 4.2+ 版本对主节点为必需) - 观察日志:
grep "compact" /var/log/mongodb/mongod.log,等待出现"Compact completed"再进行下一个 shard - 切勿并发执行多个
compact—— 单节点 IO 会打满,拖慢整个 shard 的查询响应
为什么 compact 后 df 看不到磁盘变小?
这是最常被误解的一点:compact 成功不代表 df 立即显示空间回收。它只是把“可用块”放回引擎的 available list,但文件系统层面的 .wt 文件尺寸不会自动收缩。
- WiredTiger 默认启用
file_extend_on_demand:true,所以旧文件保持原大小,新写入优先复用内部空块 - 真正缩文件要靠后续 checkpoint + background eviction 清理 discard list,通常需数小时到一两天(取决于写入压力)
- 紧急情况下可手动触发一次全量 checkpoint:
db.fsyncLock()→db.fsyncUnlock()(慎用!会短暂阻塞写入) - 更稳妥的方式是结合
db.repairDatabase()(仅限单机/非分片)或滚动重建 secondary —— 但后者才是分片集群里真正能“物理缩容”的办法
别指望 compact 解决分片集群的空洞问题——它只是局部修整,不是全局重置。真正想回收物理空间,得走 secondary 重同步或 dump/restore 流程,而那意味着你要重新评估运维窗口和客户端路由容错能力。


















