服务器入坑指北:预留空间与紧急腾挪的取舍
目录里明明还有空间,服务却报"设备上没有空间";
df显示的总量,怎么算都对不上"已用加可用";真到了要紧的时候,只能靠删日志腾地方,删完又不敢确认到底腾出来多少。
顺序是:先把"预留"这笔账算出来 → 再判断该不该动它 → 腾挪时先干跑、后动手。
这篇给三段可直接粘贴的内容:第一段只读(现读水位与算账)、第二段只读(腾挪三步,先干跑)、第三段含写操作(改预留比例并读回,标回滚点)。取值用占位符,命令名、字段名与配置键照写。
先算清"预留"这笔账
同一个挂载点上,「已用 + 可用」与「总容量」通常不相等——差额就是被预留吃掉的那块。这块预留不是损耗,而是留给特权进程的余量:普通用户能用的量来自另一个字段。
这也是"目录里还有空间、服务却说写不进去"这类现象的成因之一:报错本体是 ENOSPC(设备上没有空间),而"没有空间"要按普通用户可用量去算,不是按总空闲算。
现读与算账块
df -h <挂载点> # 现读水位:已用 + 可用 ≠ 总容量,差额就是预留
df --output=source,size,used,avail,pcent <挂载点> # 把四列摆在一起,直接做减法
stat -f -c '%b %f %a %S' <路径> # 块总数 / 全部空闲 / 非特权可用 / 块大小
tune2fs -l <设备> # 读预留块数与块大小(字段名以现读为准)
判据(两个字段名要分清):
- **
f_bfree(全部空闲块)**与f_bavail(非特权可用块)是两个不同的量——两者之差就是普通用户吃不到的那部分预留; f_frsize是换算基数:块数 × 块大小 = 容量;df与stat的数字都来自同一个系统调用(statvfs),所以它们本来就应当互相对得上——对不上说明你看的不是同一个挂载点。
算一遍:三处例证
例证一:四列做减法。 设总容量 100 GiB、已用 40 GiB、可用 55 GiB:
100 - 40 - 55 = 5(GiB)——这 5 GiB 就是预留;普通用户看到的是 55 GiB,而不是 60 GiB。
例证二:容量乘比例。 预留比例的字段名是 reserved-blocks-percentage,默认取 5%:
100 GiB × 5% = 5 GiB——与例证一算出来的差额一致。两条路必须得出同一个数,否则就是你读错了列。
例证三:块数与块大小的换算。 设块大小 4096 字节:
- 预留块数 = 5368709120 字节 ÷ 4096 = 1310720 块(其中 5368709120 字节就是 5 GiB);
- 反过来验证:1310720 × 4096 = 5368709120(字节)≈ 5 GiB;
- 于是
f_bfree - f_bavail= 1310720 块——这也就是"普通用户吃不到的那部分预留量"的块数形态。
判据:这三个数是同一条链上的三个说法(颜色、比例、块数),能互相换算,所以任何一处对不上都能反查出读错的那一列。
腾挪三步:先干跑,后动手
在清理之前先干跑一遍——systemd-tmpfiles --dry-run --clean 会只输出它打算删什么、不真的删。这条要写在动手之前。
du -x -d <层数> <路径> | sort -h # 第一步:按目录从大到小找大户
find <路径> -xdev -type f -size +<大小> # 第二步:按大小捞出候选对象
lsof +L1 # 第三步:已删除但仍被进程占用的文件
systemd-tmpfiles --dry-run --clean # 干跑:先看清要删什么,再真清
判据:
du -x -d <层数> <路径> | sort -h里的-x不跨文件系统——别把别的挂载点的占用算到这个盘上;lsof +L1的那一类特别值得注意:文件已经删了,但进程还拿着它——占用在df里看得见、在目录里找不到,这就是"删了却没腾出空间"的常见成因;- 干跑的输出与你的预期逐条对上再执行;清理依据是
Age字段(它与现读时间之差决定该不该清)。
取舍:动预留,还是腾空间
- 动预留(调低预留比例,如
tune2fs -m <百分比> <设备>):立刻多出一块可用量;代价是特权进程的余量变小——真到系统需要那点余量时,你也没有了; - 腾空间(清理、搬走大户):不动预留的语义;代价是要花时间、且可能删到有用的东西,所以要先干跑。
判据:先腾空间,再考虑动预留;动预留属高风险写操作,需回滚点——先记下当前比例,改完用tune2fs -l <设备>读回。
另外一个方向是建文件系统时就定下来:mkfs.ext4 -m <百分比> <设备>决定初始比例,事后才发现预留不够(或太多)都属于补救。
改与读回块
tune2fs -l <设备> # 改前:记录当前的预留比例与块大小
tune2fs -m <百分比> <设备> # 调预留比例(高风险写操作,需回滚点)
df --output=source,size,used,avail,pcent <挂载点> # 读回:可用量变化与算出来的差额是否一致
stat -f -c '%b %f %a %S' <路径> # 读回:f_bfree 与 f_bavail 的差有没有按预期变化
读回是独立的一步,不许省:改完要确认可用量的变化量与"容量 × 比例变化"算出来的数一致——不一致说明你改的不是那个挂载点,或者中间还有别的层。
现象到动作的判定表
| 现象 | 结论 | 动作 |
|---|---|---|
目录里还有空间却报 ENOSPC |
该看的是非特权可用量 | 用 df --output=… 摆开四列,算 f_bfree - f_bavail |
| 已用 + 可用与总量对不上 | 差额是预留 | 用 tune2fs -l <设备> 读出预留块数与总块数,再按 预留块数 / 总块数 算出比例 |
| 删了文件却没腾出空间 | 文件已删除但仍被占用 | lsof +L1 找持有者,重启或让它释放 |
| 腾挪不知道从哪下手 | 没有按量排序 | du -x -d <层数> <路径> | sort -h,再 find … -size +<大小> |
| 清理怕删错 | 没有干跑 | 先 systemd-tmpfiles --dry-run --clean,输出与预期逐条对上再执行 |
改完怎么验证:每项一条判据
- 可用量的变化与算式一致(容量 × 比例变化);
- 预留块数与"全部空闲减非特权可用"对得上;
- 大户已按量排序处理(不是凭感觉删);
- 被删除但仍被占用的文件已确认释放;
- 有回滚形态与原值记录。
这篇不解决什么
- 讲配额满了不是磁盘满的那篇是按主体计量的限额;本篇是文件系统自身的预留——一个按"谁"计,一个按"盘"计。
- 讲磁盘还有空间却写不进去的那篇是空间与 inode 耗尽的分流;本篇接住它的"预留"这一支,讲怎么算、怎么动。
- 讲容器层占用与清理的那篇是容器那一侧的占用;本篇是文件系统层的余量。
踩坑清单
- 拿总空闲当可用量:普通用户看到的是非特权可用那一项。
- 只看
-h的人读结果,不做减法:差额就是预留,不算就永远对不上。 - 把
f_bfree与f_bavail当同一个量:两者之差就是预留块。 - 忘记块数要乘块大小:
f_frsize是换算基数。 - 删了文件却没腾出空间:
lsof +L1那一类还占着。 - 直接动预留比例不留回滚点:改完说不清原来是多少。
- 用
du统计时忘了-x:把别的挂载点算进来。 - 清理不干跑:删掉之后才发现删错了。
- 忽略
Age字段:清理依据对不上,误删或漏删。 - 在还没确认是不是预留问题时就降比例:先算账,再动手。
- 只改一次不读回:不知道改的是不是那个挂载点。
- 把预留降到零当"省空间":系统的余量被一起吃掉了。
- 点赞
- 收藏
- 关注作者
评论(0)