--- title: "空间回收没卡住,它只是在等明天早上 09:29" description: "存储管理员那张卡整天写着 running,可用空间一动不动。其实 121 GiB 已经分两步回来了——然后下午又被写掉了 47 GiB。" pubDate: 2026-10-14 updatedDate: 2026-10-16 category: devops tags: ["synology", "btrfs", "snapshots", "storage", "nas"] ogImage: /og/space-reclamation-wasnt-stuck-it-was-waiting.png banner: /banners/space-reclamation-wasnt-stuck-it-was-waiting.png draft: false --- ## 我问错了问题 我清掉几个文件夹,在 NAS 上点了空间回收,然后等。几个小时后,存储管理员那张卡还写着 **running**,可用空间看起来跟早上一样。我的问题是「它为什么什么都没回收」。 **它回收过了——两次**,而且其中一次比我删掉的东西大得多。测量回答的是另一个问题,而这才是这件事里最有意思的地方。 ## 在相信任何东西之前,先给可用空间上定时采样 我没有去看那张进度卡,而是给 `volume1` 的可用空间做了当天每五分钟一次的采样: | 时间 | 可用 | 事件 | |---|---|---| | 08:59 → 09:24 | 1134.1 GiB | 平——什么都没在回收 | | **09:29** | **+69.0 GiB** | Class 快照 `…10.14-09.29.02` 轮转 | | **09:34** | **+46.8 GiB** | Video 快照 `…10.14-09.30.35` 轮转 | | **15:28:45** | — | DSM 日志:`Start to empty all recycle bins.` | | **15:34** | **+5.0 GiB** | 清回收站结束——里面只有 5 GiB | | 16:35 → 17:39 | **−39 GiB** | 新数据写入(一阵爆发) | | 15:00 → 20:44 | **−47.2 GiB** | 净值:这个卷那天下午**丢**了空间 | 两次回收,合计约 121 GiB;然后 47 GiB 的新写入把下午吃掉了。「什么都没回来」是我这边的读法错误:我拿早上和晚上比,而晚上里面多了一整天的写入。 我特意手动清空的回收站是 **5 GiB**;我压根没想到的快照是 **115.8 GiB**。这个比例就是全部故事。 ## 为什么那张卡写着 running,却什么都不代表 ```text $ synostgreclaim --get-all-status --verbose {"state":"running","num_subvolume_remain":1,"now_runnable":true} schedule_table: 168 × 1 run_time_option: run_all_time ``` `state: running` 加上 168 个 1 的调度表和 `run_all_time`,意思是这个任务被配置成**按设计 24×7 都处于 running**。它是调度状态,不是进度指示器——所以在某天完全无事可排的日子,这张卡照样这么写。而当时那台机器上**没有** `synostgreclaim` 进程、没有 btrfs balance、md2/dm-3 的 I/O 基本为零。那一刻没有任何东西在被回收,也没有任何东西卡住。 然后是日志,而 DSM 上它们大多是红鲱鱼: - 回收任务本身什么都不记; - `/var/log/space_operation.log` 是 LUKS 和卷列表的噪音; - `/var/log/synorecycle.log` 是 `synounlinkd` 的路径噪音; - 当天的 Log Center 里只有我自己那条清回收站的记录,外加几条 UPS 断电/来电的提示。 一个永远为真的 UI 状态字段,加上三个永远不会给出答案的日志文件:唯一剩下的仪器,就是**随时间变化的可用空间**。 ## 没人告诉你的那个机制 空间回收释放的是**已删除的 btrfs 子卷**。它释放不了仍然被快照钉住的数据。那两个共享文件夹的保留策略是 **retention 1**(各留一代),所以**我删掉的任何东西,都要等到第二天的轮转、大约 09:29 / 09:30 才会变成可用空间**,一次跳几十 GB,然后安静 24 小时。 那两次跳变正是这个。它也就意味着,对于「我删了 50 GB 却什么都没发生」这个问题,在这台机器上的诚实答案是:「等明天早上再看」——而如果删掉的数据本来就在某个快照里面,答案是「等那个快照过期,之前不会释放」。 ## 如果重来一次 1. **采样那个数字,不要采样状态。** 每五分钟量一次可用空间,比任何任务卡都更能回答「现在有没有事情在发生」,而且它同时让「回长」变得可见。 2. **读调度字段,不读状态字段。** `run_all_time` 解释了那个永久 running,而且不需要任何 bug 存在。 3. **搞清楚每个共享文件夹的保留策略。** 它是决定「删除何时变成可用空间」的那口钟;不知道它,每一次删除都会看起来坏掉一整天。 4. **把两本账分开。** 回收站(5 GiB)和快照(115.8 GiB)行为完全不同,也是两套心理模型。 5. **重新检查分母。** 这个卷平时就在以约 1.5 GiB/时 的速度被正常写入吃掉;回收回来 121 GiB、当天又花掉 47 GiB,只要你只比同一个数字的两个快照,这两件事看起来完全一样。 ## 测量真正告诉我的,是这台机器的趋势 我本来是去修一个卡住的任务,结果带回来的是一条存储趋势: ```text volume1 42 TB 已用 98% 剩 1.2 TB 近来约 -1.5 GiB/时(8 天 -344 GiB) volume2 16 TB 已用 100% 剩 70 GB 约 -19 GiB / 2 天 健康 两个卷 btrfs corruption_errs 均为 0 所有 md 阵列 [UUUU] / [UU],没有在跑 balance read/write_io_errs 与九月基线一致 ``` 所以有用的结论不是「回收是好的」。而是:**相对于写入速率,回收只是一个舍入误差**;在保留策略为 1 的前提下,删除并不是我以为的那根杠杆;而该拿来做告警的是趋势,不是那张任务卡。这跟[《你的磁盘快满告警在撒谎》](/posts/your-disk-full-alert-is-lying/)是同一课,只是从另一个方向来的:对**后果**告警(随时间变化的可用字节),因为机制自己的状态不是证据。