用 df -h 看到根分区 90%,用 du -sh /* 累加起来却只有一半?这几乎是每个运维都遇到过的困惑。先说结论:
df和du统计口径不同,对不上是正常现象。df看的是文件系统层已使用的块;du是把当前能看到的每个文件实际大小累加。两者只在"一切文件都可见、无被删占用、无隐藏挂载"的理想状态下才相等。
这篇文章讲清楚差异来源,并给出一套逐步定位"空间到底去哪了"的方法。
先分清两个命令的口径
| 命令 | 统计对象 | 特点 |
|---|---|---|
df | 整个文件系统/分区的块使用情况 | 包含所有占用的块,含元数据、被删但仍被进程占用、被隐藏挂载挡住的 |
du | 从当前目录遍历到的可见文件/目录 | 只算能遍历到的,mount 挂载点下的内容默认也算它自己一份 |
举一个极端例子:某进程正在写一个大文件,你把它删了但进程没退出——磁盘块并未释放,df 显示还是满的,而 du 已经"看不到"那个文件了。这是最常见的"对不上"来源之一。
用这个顺序排查,找出空间去哪了
第 1 步:确认是 df 大还是 du 大
df -h /
du -xh --max-depth=1 / 2>/dev/null | sort -h | tail -20
提示:
du -x表示不要跨文件系统去统计(避免把/proc、挂载的独立分区也算进来),--max-depth=1只列一层。
第 2 步:被删除但仍被进程占用的文件(最常见)
如果 df 明显大于 du,优先查"删了但没释放"的文件:
lsof +L1 2>/dev/null | grep -iE 'deleted|unlinked'
+L1 列出所有 link count 为 0(已被删除)却仍被打开的文件。找到后定位进程:
lsof +L1 2>/dev/null | grep -i deleted
# 记下第 2 列的 PID,再用 ps 看是谁
ps -fp <PID>
处理方式:让对应服务正常重启(释放句柄)即可归还空间;如果无法重启,只能视情况处理该进程。确认归还:
df -h /
第 3 步:有没有隐藏/重复的挂载点
同一目录被二次挂载(或某个分区挂到了 du 遍历的目录下),du 会重复计算或漏算。查看所有挂载与占用:
mount | grep -E '^/dev' | sort
findmnt
# 想看清每个挂载点占了多少,用 -x 从上往下看
df -h
第 4 步:稀疏文件与预分配
用 truncate 或数据库预分配的文件,du 显示的是"实际占用",而 ls -lh 显示的是"逻辑大小",两者也能差很大。检查有无大逻辑文件:
ls -lhS /path/to/check 2>/dev/null | head
如果确认是大文件且不再需要,用 truncate 收缩即可(对正在写的文件慎用)。
快速对比示例
# df 角度
df -h /
# du 逐目录角度(最多 20 行、按大小排序)
du -xhd1 / 2>/dev/null | sort -h | tail -20
# 谁占用被删文件
lsof +L1 2>/dev/null | grep deleted
常见结论速记
df大、du小 → 多半是被删未释放(lsof +L1)或隐藏挂载。du大、df小(不太常见)→ 目录下有另一个独立挂载点被du重复计入,或跨文件系统统计没加-x。- 两个都大 → 确实有海量小文件(占 inode)或文件系统元数据占用,见 inode 篇。
FAQ
问:为什么删了文件 df 空间没变?
因为文件仍被某进程打开(句柄未释放),只有关闭该进程/重启服务后块才会归还。用 lsof +L1 找出来。
问:du 为什么比 df 看到的少很多?
du 只统计当前能遍历到的可见文件,被删占用、/proc 之类虚拟文件系统、或隐藏挂载的内容都不会算进去。
问:怎样最直观地看每个目录占用?
du -xhd1 / | sort -h | tail 从根往下逐层找大目录,逐层进入即可定位大头。
想进一步清理空间或看懂 inode,可看同系列:磁盘空间满了却删不掉 / inode 耗尽 / 磁盘满了怎么办清理实战。