compact在mongos上直接报错是因为mongos不支持跨分片协调该操作,且不转发compact命令,必须登录目标shard的primary节点执行。

deleteMany 或 remove 不会释放磁盘空间,直接执行 compact 在分片集群上会失败,且在副本集主节点上需加 force: true 才能生效。
为什么 compact 在 mongos 上直接报错?
分片集群中,mongos 不转发 compact 命令——它压根不支持跨分片协调该操作。你执行 db.runCommand({compact: "coll"}) 会立刻收到:"command compact requires authentication as a member of the clusterAdmin role",即使权限满足,mongos 也会拒绝,因为底层每个 shard 是独立 WiredTiger 实例,compact 必须在具体分片的 mongod 进程内运行。
所以第一步必须确认你的部署类型:
- 单节点或副本集 → 可直接在 primary 或 secondary(设为 hidden)上执行
compact - 分片集群 → 必须登录目标
shard的 replica set primary 节点,且先停均衡器、迁移 chunk、临时下线该 shard
在副本集上安全执行 compact 的实操要点
即便在副本集,也不能在 primary 上随意跑 compact:它虽不阻塞 CRUD(4.4+),但会阻塞建索引、删集合等元数据操作,且可能加剧主从同步延迟。
- 优先选 secondary:用
rs.secondaryOk()或连接时加?readPreference=secondary,再执行db.runCommand({compact: "coll", force: true}) - 务必加
force: true:否则 WiredTiger 可能跳过压缩(例如空闲空间未达阈值或有活跃 snapshot) - 检查效果不能只看
storageSize:要对比db.coll.stats().storageSize / db.coll.stats().size比值,>2.5 才说明碎片显著;更准的是查wiredTiger["block-manager"]["file bytes available for reuse"] - 别在业务高峰做:compact 期间 I/O 和内存压力上升,WiredTiger cache 可能被冲刷,导致后续查询变慢
分片集群下真正可行的收缩路径
分片集群没有“在线 compact”这回事。所谓“收缩”,本质是重建数据文件。你只有三个现实选项:
-
离线
--repair:停掉目标 shard 的mongod,用mongod --dbpath /data --repair启动(WiredTiger 下等效于重建 .wt 文件),但需双倍磁盘空间,且服务中断 -
mongodump+mongorestore --drop:在线可做,但 restore 期间新写入会被丢弃(除非配合应用层暂停写),且对大集合耗时长、网络和 CPU 压力大 -
TTL 索引替代批量删除:日常防碎片比事后收缩更重要。把
deleteMany({ts: {$lt: ...}})改成createIndex({ts: 1}, {expireAfterSeconds: ...}),让后台线程平滑清理,避免一次性标记删除引发 page fragmentation
最容易被忽略的一点:WiredTiger 默认不把空闲页还给操作系统,而是留在 cache 里复用。所以即使 compact 成功,df -h 看到的磁盘使用率也可能不变——这是正常现象。真要逼它交还,只能靠 mongod 重启(触发 cache 清理)或极端下调 wiredTigerEngineConfigString 中的 cache_size(不推荐)。


















