当磁盘满了,df和du却“各执一词”,真相只有5个!
df 和 du,磁盘空间的 “双面镜”
在 Linux 系统运维的领域中,磁盘空间管理就像是守护系统稳定运行的基石。我们时常会依赖df和du这两个命令来查看磁盘的占用情况,可有时它们却像是两个闹矛盾的伙伴,给出截然不同的结果。明明df显示磁盘已经 100% 占满,du却告诉我们空间似乎还很充裕 ,这种差异不仅让人困惑,更可能在生产环境中埋下隐患。比如,当服务器磁盘空间不足时,可能导致应用程序无法写入日志、数据库无法扩展,进而影响整个业务的正常运转。所以,搞清楚这背后的原因至关重要,接下来就让我们一起深入探究。
df 与 du 的本质剖析
df:系统之眼,洞察全局
df,即 “Disk Free” 的缩写 ,堪称 Linux 系统中查看磁盘空间的 “全局探测器”。它的工作原理是读取文件系统的元数据,这些元数据就像是磁盘的 “户口本”,详细记录了磁盘分区的各种信息。通过这些元数据,df能够精准地统计出磁盘分区的整体使用情况,包括那些隐藏在系统深处、用户难以直接察觉的 “不可见” 文件所占用的空间 。这就好比你在查看一个小区的居住情况,df不仅能告诉你住了多少户人家,还能把那些被遗忘在角落里的储物间等空间都统计在内。
在实际使用中,df的用途十分广泛。比如,系统管理员可以通过它快速了解服务器磁盘的整体使用状况,提前预警磁盘空间不足的情况。当我们执行df -h命令时,会得到一个以人类可读格式展示的磁盘分区信息列表,其中包括每个分区的总大小、已使用空间、可用空间、使用率以及挂载点。例如:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 30G 20G 60% /
tmpfs 4.0G 0 4.0G 0% /dev/shm这里,/dev/sda1是磁盘分区,总大小为 50GB,已使用 30GB,剩余 20GB,使用率为 60%,挂载在根目录 “/” 下;tmpfs是临时文件系统,大小为 4GB,目前未使用,挂载在/dev/shm。-h参数是df命令中非常常用的,它让输出结果以 KB、MB、GB 等更易读的单位展示,大大方便了我们对磁盘空间信息的快速理解。 此外,df还有其他一些实用参数,比如-T可以显示文件系统类型,这在排查特定文件系统相关问题时非常有用;-i能按 inode 使用情况显示,帮助我们排查 inode 被耗尽的问题,即使磁盘空间看似充足,但如果 inode 用完了,也无法创建新文件。
du:用户视角,聚焦细节
du,也就是 “Disk Usage”,是从用户实际可访问的角度来统计磁盘使用情况的工具。它的工作方式是遍历实际的目录树,逐个检查目录下的文件,并调用fstat这个系统调用来获取文件大小。与df不同,du仅统计当前目录下用户可访问的文件大小,对于那些因为权限不足或其他原因无法访问的文件,它是不会纳入统计范围的。这就像是你在清点自己房间里的物品,你只能统计自己能看到、能接触到的东西,那些被锁在柜子里或者别人房间里的物品,你是无法统计的。
在实际运维中,du常用于定位占用磁盘空间较大的文件或目录。比如,当我们怀疑某个目录下存在大文件占用过多磁盘空间时,可以使用du -sh /path/to/directory命令来查看该目录的总大小。其中,-s参数表示只显示总计,-h参数同样是为了让输出结果更易读。如果我们想要深入了解某个目录下各个子目录的空间占用情况,可以使用du -h --max-depth=1 /path/to/directory命令 ,--max-depth=1表示只深入到指定目录的下一层子目录,这样就能快速看到每个子目录的大小,从而方便我们进一步排查大文件所在。例如:
du -h --max-depth=1 /var
4.0K /var/cache
8.0K /var/log
12G /var/lib从这个结果中,我们可以清晰地看到/var目录下各个子目录的大致占用空间,像/var/lib目录占用了 12GB,这就提示我们需要重点关注这个目录,看看里面是否有可以清理的文件。此外,du还可以通过-a参数显示目录中个别文件的大小,-c参数同时显示所有目录或文件的总和等,这些参数组合可以满足我们在不同场景下对磁盘空间统计的需求。
5 种让 df 和 du “打架” 的情况
当df和du给出不一致的结果时,就像是一场神秘的磁盘空间谜题等待我们去解开。下面,就让我们详细探讨导致这种差异的 5 种常见情况。
情况一:删除文件,空间却 “赖着不走”
在 Linux 系统中,文件的删除操作并不总是像我们想象的那样简单直接。当我们使用rm命令删除文件时,系统实际上只是删除了文件的目录项,也就是文件的索引信息 。如果此时有进程仍然持有这个文件的句柄,那么文件的数据块并不会被真正释放,磁盘空间也就不会被回收 。这就好比你把一本书从书架上的目录中划掉了,但书本身还在书架上占着位置,别人看不到这本书在目录里,但它实际还占用着空间。
在实际场景中,这种情况经常出现在日志文件的处理上。比如,一个正在运行的 Web 服务器(如 Nginx 或 Apache),它的日志文件不断增大,占用了大量磁盘空间。管理员为了释放空间,删除了日志文件,但发现磁盘空间并没有减少。这是因为 Web 服务器进程还在不断向这个已经被删除的日志文件写入新的日志数据,文件句柄被进程牢牢抓住,导致文件无法被彻底删除。我们可以通过lsof命令来验证这个问题,lsof | grep deleted这个命令会列出所有已经被删除但仍被进程占用的文件。例如:
lsof | grep deleted
nginx 1234 root 12w REG 253,1 10485760 123456 /var/log/nginx/access.log (deleted)从这个结果中可以看到,nginx进程(进程 ID 为 1234)正在写入一个已经被删除的日志文件/var/log/nginx/access.log。解决这个问题的方法有两种:一种是杀掉占用文件的进程,使用kill -9 进程ID命令,但这种方法可能会导致服务中断,影响业务正常运行;另一种更推荐的方法是重启相关服务,比如对于 Nginx 服务,可以使用systemctl restart nginx命令,这样既能释放文件句柄,回收磁盘空间,又能保证服务的持续运行。
情况二:挂载点 “搞鬼”,数据 “隐身”
在 Linux 系统中,挂载点的操作就像是给文件系统穿上了一层 “隐身衣”,可能会导致df和du的统计结果出现差异。当我们将一个新的磁盘或分区挂载到一个已存在文件的目录上时,新挂载的文件系统会覆盖原目录下的所有文件,原文件就像是被 “藏” 了起来 。此时,du命令只能统计新挂载文件系统中的文件大小,而df命令会统计整个挂载点所在分区的空间使用情况,包括那些被隐藏的原文件所占用的空间 。这就好比你在一个装满东西的房间里又放了一个巨大的箱子,箱子盖住了房间里的一部分东西,你能看到箱子里的东西(新挂载文件系统的内容),但看不到被箱子盖住的东西(原文件),而整个房间的空间(挂载点所在分区)还是被所有东西占用着。
假设我们原本在/data目录下有一个占用 10GB 空间的大文件bigfile.dat,后来我们挂载了一个新的磁盘分区到/data目录,新磁盘分区里只有一些小文件,总共占用 1GB 空间。当我们使用du -sh /data命令时,它只会统计新挂载磁盘分区里的小文件,显示结果可能是 1GB;而使用df -h命令查看/data目录所在分区时,它会计算整个分区的使用情况,包括被覆盖的bigfile.dat,显示的已使用空间可能还是 10GB 以上 。要验证这个问题,我们可以通过mount命令检查当前的挂载情况,看看是否存在异常挂载。例如:
mount | grep data
/dev/sdb1 on /data type ext4 (rw,relatime)从这个结果中可以看出/dev/sdb1被挂载到了/data目录。然后,我们可以尝试临时卸载这个挂载点,使用umount /data命令,再查看/data目录下的文件,可能就会看到那个消失的大文件。解决方案就是临时卸载挂载点,删除那些被隐藏的大文件,然后重新挂载新的磁盘分区。如果不想覆盖原文件,可以创建一个新的目录作为挂载点,避免这种冲突。最后,别忘了更新/etc/fstab文件,设置永久挂载,确保系统重启后挂载点依然正确。
情况三:inode 耗尽,空间 “假满”
在 Linux 文件系统中,inode 就像是文件的 “身份证”,它存储着文件的元数据信息,如文件的权限、所有者、大小、创建时间等 。每个文件和目录都需要占用一个 inode,当系统中的 inode 被耗尽时,即使磁盘还有足够的剩余空间,也无法创建新的文件或目录 。这就好比一个图书馆里虽然还有很多空位可以放书,但图书馆的书架编号(inode)已经全部被占用完了,新进来的书没有编号可以对应,也就无法被放置在书架上。
当 inode 耗尽时,我们会遇到一些奇怪的现象,比如执行创建文件的操作时,系统会提示 “磁盘空间不足”,但使用df -h命令查看磁盘空间,却发现还有大量的可用空间 。要检查 inode 的使用情况,我们可以使用df -i命令,它会显示每个文件系统的 inode 总数、已使用的 inode 数、剩余的 inode 数以及 inode 的使用率 。例如:
df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 102400 102400 0 100% /从这个结果中可以看到,/dev/sda1分区的 inode 已经被 100% 占用,没有剩余的 inode 可用。为了排查是哪个目录下存在大量小文件导致 inode 耗尽,我们可以使用find命令结合wc -l命令来统计文件数量。比如,find /var -type f | wc -l这个命令会统计/var目录下的所有普通文件数量,如果发现某个目录下的文件数量异常多,就需要重点关注这个目录。解决 inode 耗尽问题的方法有很多。首先,可以清理一些缓存目录,如/var/cache,里面通常会存放大量的临时文件,可以使用rm -rf /var/cache/*命令来删除这些文件,释放 inode 资源 。其次,优化程序代码,避免程序在运行过程中生成大量不必要的小文件。最后,如果文件系统支持,还可以考虑重新格式化文件系统,在格式化时调整 inode 的分配数量,或者更换为 inode 分配更合理的文件系统,如 XFS 文件系统,它在 inode 的管理上相对更灵活,能更好地应对大量小文件的场景。
情况四:文件系统损坏或错误
文件系统就像是磁盘的 “管家”,负责管理文件的存储和读取。当文件系统损坏或出现错误时,就像是管家 “乱了套”,会导致df和du命令获取到的数据不一致,甚至出现文件丢失、无法访问等严重问题 。文件系统损坏的原因有很多,比如突然断电、硬件故障、磁盘 I/O 错误等,这些情况都可能破坏文件系统的关键数据结构,如超级块、inode 表等 。其中,超级块是文件系统的核心元数据,它存储着文件系统的基本信息,如文件系统的大小、块大小、inode 总数等,如果超级块损坏,df命令就无法准确获取文件系统的空间使用情况,可能会导致显示的磁盘空间与实际情况不符。
当怀疑文件系统损坏时,我们需要使用专门的文件系统修复工具来检查和修复问题。在 Linux 系统中,常用的文件系统修复工具是fsck(File System Check) 。对于不同类型的文件系统,fsck有相应的变体,比如对于 ext2、ext3、ext4 文件系统,使用e2fsck;对于 XFS 文件系统,使用xfs_repair 。在使用fsck工具时,需要注意以下几点:首先,要确保文件系统处于卸载状态,因为在文件系统挂载时进行修复可能会导致数据丢失或进一步损坏文件系统 。例如,如果要修复/dev/sda1分区的文件系统,需要先使用umount /dev/sda1命令卸载该分区,然后再执行fsck /dev/sda1命令进行修复 。其次,fsck命令可能会提示一些交互性的问题,如是否修复某个错误,在回答这些问题时要谨慎,尽量按照默认建议操作,除非你非常清楚自己在做什么 。修复完成后,可以重新挂载文件系统,再使用df和du命令检查磁盘空间是否恢复正常。
情况五:隐藏文件或特殊文件作祟
在 Linux 系统中,存在一些隐藏文件和特殊文件,它们就像是磁盘空间统计中的 “隐藏玩家”,可能会导致df和du的显示结果出现差异 。隐藏文件是以点(.)开头的文件,它们在普通的文件列表中是不会显示的,需要使用ls -a命令才能查看 。这些隐藏文件可能是系统配置文件、应用程序的缓存文件等,它们虽然隐藏起来,但同样会占用磁盘空间 。特殊文件则包括设备文件、链接文件等,设备文件用于与硬件设备进行交互,如/dev/sda表示第一个 SCSI 磁盘设备;链接文件又分为硬链接和软链接,硬链接是多个文件名指向同一个文件数据块,软链接则是一个文件指向另一个文件的路径 。这些特殊文件的大小和占用空间的计算方式与普通文件不同,可能会影响df和du的统计结果。
当我们使用du命令统计目录大小时,如果不使用-a参数,它默认不会统计隐藏文件的大小,这就可能导致du的结果比实际占用空间小 。比如,在/home/user目录下有一个隐藏的缓存目录.cache,里面存放了大量的缓存文件,占用了 1GB 空间。当我们使用du -sh /home/user命令时,它不会统计.cache目录,显示的结果可能会比实际占用空间小很多。如果使用du -ash /home/user命令,就能看到包括隐藏文件在内的所有文件的大小 。对于特殊文件,我们需要分析它们对磁盘空间的实际占用情况。例如,设备文件本身并不占用磁盘空间,它们只是提供了一种访问硬件设备的接口;硬链接文件虽然有多个文件名,但实际只占用一份文件数据块的空间,df和du在统计时会正确处理硬链接,不会重复计算空间 。而软链接文件比较特殊,它本身只是一个指向目标文件的路径,占用的空间非常小,通常只有几个字节,但如果软链接指向的目标文件很大,du命令在统计时会跟随软链接指向的目标文件,统计目标文件的大小,这可能会导致统计结果与预期不符 。如果发现某些特殊文件占用了过多磁盘空间,需要根据具体情况进行处理。对于无用的缓存文件等隐藏文件,可以直接删除;对于软链接,如果它指向的是一个已经不存在的文件(即 “死链接”),可以删除软链接,避免混淆;对于设备文件,如果出现异常占用空间的情况,可能需要检查硬件设备是否存在故障。
经验总结
模拟场景,排查问题
为了让大家更深入地理解上述 5 种导致df和du结果不一致的情况,我们不妨搭建一个模拟环境,来实际演练一下问题的排查过程。
假设我们有一台运行 CentOS 7 的服务器,上面有一个/data目录,挂载在/dev/sda2分区上 。首先,模拟删除文件但空间未释放的情况。我们使用dd命令创建一个大小为 1GB 的大文件testfile.dat,命令如下:
dd if=/dev/zero of=/data/testfile.dat bs=1G count=1
然后使用rm命令删除这个文件:
rm -rf /data/testfile.dat
此时,使用df -h命令查看,会发现磁盘空间并没有减少,因为文件虽然被删除,但可能有进程还在占用它 。接着,使用lsof | grep deleted命令验证,假设我们发现有一个httpd进程(进程 ID 为 1234)正在占用这个已删除的文件,如下所示:
lsof | grep deleted
httpd 1234 apache 12w REG 253,2 1073741824 123456 /data/testfile.dat (deleted)这就验证了文件被删除但空间未释放的情况。
接下来,模拟挂载点覆盖原数据的情况。先在/data目录下创建一个小文件smallfile.txt,然后挂载一个新的磁盘分区/dev/sdb1到/data目录 。使用mount /dev/sdb1 /data命令完成挂载后,再使用du -sh /data命令查看,会发现它只统计了新挂载分区里的文件大小,而df -h命令查看/data目录所在分区时,会包含原文件占用的空间。通过mount命令检查挂载情况,能看到/dev/sdb1已挂载到/data,此时临时卸载挂载点umount /data,再查看/data目录,就能看到之前被覆盖的smallfile.txt。
对于 inode 耗尽的模拟,我们可以在/data目录下编写一个简单的 Python 脚本,用于生成大量的小文件 。脚本内容如下:
import os
for i in range(100000):
  with open(f"/data/smallfile\_{i}.txt", "w") as f:
  f.write("This is a small file.")运行这个脚本后,使用df -i命令检查 inode 使用情况,会发现 inode 很快被耗尽 。接着,使用find /data -type f | wc -l命令统计/data目录下的文件数量,会看到文件数量非常多,从而确定是该目录下的大量小文件导致了 inode 耗尽。
模拟文件系统损坏的情况比较复杂,我们可以使用badblocks工具人为地在模拟磁盘分区上制造一些坏块 。假设我们的模拟磁盘分区是/dev/sda2,首先卸载该分区umount /dev/sda2,然后使用badblocks -w /dev/sda2命令制造坏块(此操作会损坏数据,仅在模拟环境中进行) 。重新挂载分区后,使用df -h和du -sh命令查看,可能会发现两者结果不一致。此时,使用fsck /dev/sda2命令进行文件系统修复,修复过程中按照提示操作,修复完成后再次检查磁盘空间,看是否恢复正常。
最后,模拟隐藏文件和特殊文件的情况。在/data目录下创建一个隐藏文件.hiddenfile.txt和一个软链接文件symlink.txt,软链接指向系统中一个较大的文件 。使用du -sh /data命令查看时,如果不使用-a参数,它不会统计隐藏文件的大小;而对于软链接文件,du命令会跟随链接指向的目标文件统计大小,这可能会导致统计结果与预期不符。通过ls -a /data命令查看隐藏文件,使用ls -l /data命令查看软链接文件的详细信息,就能分析出特殊文件对磁盘空间统计的影响。
经验之谈,避免踩坑
在实际运维工作中,遇到df和du结果不一致的情况时,除了掌握上述排查和解决方法外,还有一些经验和注意事项可以帮助我们少走弯路。
养成定期清理日志文件的习惯非常重要 。许多服务都会产生大量的日志文件,如果不及时清理,这些文件会占用大量磁盘空间。可以设置日志轮转策略,让系统定期自动清理旧日志,同时保留一定时间内的重要日志 。例如,对于 Nginx 服务,可以通过修改/etc/logrotate.d/nginx配置文件来设置日志轮转策略:
/data/logs/nginx/\*.log {
  daily
  missingok
  rotate 7
  compress
  delaycompress
  notifempty
  create 640 nginx adm
  sharedscripts
  postrotate
  /usr/bin/systemctl reload nginx > /dev/null 2>/dev/null || true
  endscript
}这段配置表示/data/logs/nginx目录下的日志文件每天轮转一次,保留 7 天的日志,轮转时进行压缩,并且在轮转后重新加载 Nginx 服务,确保新的日志文件正常生成。
合理规划磁盘分区也是关键 。在服务器部署初期,根据业务需求合理划分磁盘分区,避免某个分区因为空间不足而出现问题。比如,将数据存储目录、日志目录、系统文件目录等分别划分到不同的分区,这样可以更方便地管理磁盘空间,也便于排查问题。同时,要注意预留一定的磁盘空间作为缓冲,以应对突发情况,比如业务高峰期数据量的突然增加。
监控 inode 的使用情况也不容忽视 。可以使用一些监控工具,如 Zabbix、Prometheus 等,设置 inode 使用率的告警阈值,当 inode 使用率达到一定程度时,及时发出警报,以便管理员提前采取措施,避免 inode 耗尽导致系统故障 。例如,在 Zabbix 中,可以创建一个自定义监控项,监控df -i命令的输出结果,设置当 inode 使用率超过 80% 时触发告警。
在处理磁盘问题时,一定要谨慎操作 。特别是在删除文件时,要确保文件确实不再需要,并且没有进程在占用它 。对于重要数据,务必提前进行备份,以免误操作导致数据丢失。比如,在清理磁盘空间时,不要随意使用rm -rf命令删除整个目录,应该先使用du -h命令查看目录下的文件大小,确定哪些文件可以删除,然后再逐一删除。如果需要删除大量文件,可以先将文件移动到一个临时目录,观察一段时间后,确认没有问题再彻底删除。同时,在进行文件系统修复等操作时,要仔细阅读操作提示,按照正确的步骤进行,避免因为操作不当而导致更严重的问题。