df -h 看到根分区 90%,用 du -sh /* 累加起来却只有一半?这几乎是每个运维都遇到过的困惑。先说结论:

dfdu 统计口径不同,对不上是正常现象。 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 耗尽 / 磁盘满了怎么办清理实战。