bigywater.tech
MongoDB · 17

删了数据磁盘为什么不还:compact 前后对比

删了大半数据,磁盘占用为什么没变?这篇用 12 万个文档的集合,对比 deleteMany 前后的 dataSize、storageSize、磁盘文件大小和可复用空闲空间,再看 compact 和 drop 各自能还多少。

+4.8 MB
deleteMany 删掉 90% 的文档后,磁盘文件反而变大的字节数
45.5 → 9.2 MB
compact 前后集合磁盘文件的大小,bytesFreed 36,347,904
0 个错误
compact 期间另一线程 6,018 次读和 6,018 次写没有失败

插入之后

实测 · 本机执行输出
                              文档数     dataSize  storageSize         磁盘文件        可复用空闲       索引总大小
  插入后                     120,000   39,720,000   40,747,008   40,747,008       16,384   2,625,536

12 万个文档约 39.7 MB,磁盘文件 40.7 MB,几乎没有可复用空闲。

删除不还磁盘

实测 · 本机执行输出
  delete_many 删除 108,000 个,耗时 0.3 s
                              文档数     dataSize  storageSize         磁盘文件        可复用空闲       索引总大小
  删除后                      12,000    3,972,000   45,527,040   45,527,040   40,726,528   3,284,992
  磁盘文件 40,747,008 → 45,527,040(变化 +4,780,032 字节);可复用空闲 16,384 → 40,726,528

删掉 90% 后 dataSize 降到约 4 MB,磁盘文件却没变小(反而 +4.8 MB),多出来的 40.7 MB 变成“可复用空闲”留在文件里。

空闲会被复用

实测 · 本机执行输出
                              文档数     dataSize  storageSize         磁盘文件        可复用空闲       索引总大小
  再插入 10 万个后              112,000   37,072,000   45,522,944   45,522,944    6,574,080   3,284,992
  磁盘文件 45,527,040 → 45,522,944(-4,096 字节);可复用空闲 40,726,528 → 6,574,080

再插入 10 万个文档,文件大小几乎不变,可复用空闲降到 6.6 MB:删除的空间留给了后来的写入。

compact

实测 · 本机执行输出
  又删除了 90,000 个
                              文档数     dataSize  storageSize         磁盘文件        可复用空闲       索引总大小
  删除后                      22,000    7,282,000   45,522,944   45,522,944   36,401,152   3,297,280
  compact 返回 {'bytesFreed': 36347904, 'ok': 1.0}(耗时 0.2 s)
  compact 后                22,000    7,282,000    9,175,040    9,175,040       53,248   3,297,280
  磁盘文件 45,522,944 → 9,175,040(减少 36,347,904 字节,80%);索引总大小 3,297,280 → 3,297,280

compact 把空闲空间还给操作系统:文件从 45.5 MB 降到 9.2 MB。这个实验里索引总大小在 compact 前后没有变化。

compact 期间的读写

实测 · 本机执行输出
  compact 期间(含前后各 0.5 s)的 read: 6018 次,中位数 0.11 ms,最大 5 ms
  compact 期间(含前后各 0.5 s)的 write: 6018 次,中位数 0.10 ms,最大 1 ms
  compact 本身耗时 0.4 s;期间工作线程的错误数: 0 []

在这个 12,000 文档的小集合上,compact 0.4 s,期间同一个集合的读写没有错误,最大延迟 5 ms。大集合上的影响没有测。

drop 与 deleteMany

实测 · 本机执行输出
  t3 插入 50,000 个: 磁盘文件 16,289,792
  deleteMany({}) 之后: 文档数 0,磁盘文件 16,293,888,可复用空闲 16,285,696
  drop 之后文件还在吗: False

deleteMany({}) 之后文件还在,全是空闲空间;drop 才会立刻删文件。

分步演示

删数据与磁盘空间
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MongoDB 9.0.2(Homebrew 的 mongod、mongos)/ mongosh 2.12.0 / pymongo 4.18.3;每个实验在临时目录里起全新的 mongod(独立 dbpath,端口 9900 以上,只监听 127.0.0.1,WiredTiger 缓存 0.25 GB),需要副本集或分片集群时在本机起多个进程;命令输出来自真实的 mongosh,批量写入、计时与多线程用 pymongo;计时用 time.perf_counter,数字每次运行会有小幅抖动,ObjectId、时间戳等每次运行都不同。每一步都先 fsync 再读 collStats 和 .wt 文件的实际大小;“可复用空闲”是 collStats 里 wiredTiger.block-manager 的 file bytes available for reuse
    • 没有验证:compact 对大集合(GB 级)耗时与对前台读写的影响;compact 是否会重建索引(这里索引总大小没变,原因没有深究);副本集上对 secondary 逐个 compact 的流程;删除时文件为什么增长了 4.8 MB(只观察到现象);TTL 删除与 capped 集合的空间回收;initial sync 或 resync 重建磁盘文件的做法
    京ICP备2026067502号-1京公网安备11010502063487号