ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Linux磁盘空间排查:用du和df定位磁盘占用的实用指南

2026/9/19 11:25:42 拓冰建站 浏览量
Linux磁盘空间排查:用du和df定位磁盘占用的实用指南 开头“磁盘又满了”——这大概是Linux运维和日常使用中听得最多的一句话。不管你是刚装好系统的桌面用户还是管着一堆服务器的运维早晚会遇到一个分区莫名其妙被撑爆、但你死活想不起来是什么东西占掉空间的情况。这时候最常用的两个命令就是du和df。别看这俩命令短小真要说得头头是道、用得明明白白还真有不少门道。这篇文章就正儿八经地聊聊怎么用du查看当前路径下各个文件夹的大小用df看磁盘剩余空间顺带把我这些年踩过的坑、总结出的排查套路一并交代清楚适合所有被磁盘空间问题困扰过的Linux使用者参考。先说个大概du是Disk Usage的缩写核心作用是统计文件和目录占用了多少磁盘块也就是“用量”df是Disk Free的缩写核心作用是看文件系统整体的空间使用情况也就是“剩余量”。一个从目录树内部往下钻一个从文件系统层面往上看两者配合起来才能把磁盘空间问题看得通透。下面我按从原理到实操、从简单到进阶的顺序把这一整套东西掰开揉碎讲清楚。1. 先搞清楚du和df到底在统计什么1.1du统计的是“目录树里所有文件的逻辑占用”用du统计文件夹大小时它做的事是递归遍历指定目录下的所有文件把每个文件占用的磁盘块累加起来。注意这里说的是“磁盘块”不是文件的实际字节大小。因为文件系统是按块block分配空间的一个4KB的块就算只存了1字节也要占满整个4KB。所以你经常会看到du算出来的结果比ls -l看到的文件大小要大这在大量小文件的情况下尤其明显。还有一个很多人不知道的细节du默认统计的是文件的“实际占用空间”也就是考虑了硬链接、稀疏文件这些特殊情况后的结果。硬链接会让同一个inode被多个文件名引用du在统计时默认只计算一次稀疏文件比如某些数据库文件、虚拟机磁盘镜像逻辑上很大但实际只占了一部分块du统计的是真实分配的块而不是文件末尾减去开头的那个“空洞”大小。1.2df统计的是“文件系统层面的空间余量”df看的角度完全不一样。它直接读取文件系统的超级块superblock和块组描述符拿到整个文件系统的总块数、已用块数、可用块数然后换算成人类可读的数字。它不关心目录树长什么样也不关心文件是谁只看这个挂载点对应的那块“地盘”整体还剩多少。所以df -h输出里那个/dev/sda1或者/dev/mapper/centos-root对应的是一个具体的块设备或者逻辑卷而不是某个目录。df统计的“已用”包括了文件系统自身的元数据开销比如inode表、块位图、日志journal等等这些在du里是看不到的。这也是du和df结果经常对不上的第一个原因。1.3 为什么du加出来的总大小和df显示的已用空间差了那么多这个问题我见过无数人困惑。你辛辛苦苦把所有目录的du -sh结果加起来发现只有80GB但df告诉你已经用了120GB剩下的40GB去哪了答案可能有这么几个文件系统元数据开销inode表、块位图、日志文件这些都要占空间但du根本不会统计它们。被删除但还被进程占用的文件这算是经典中的经典。进程打开了一个文件你把它rm了但进程还在持续写入这个文件的空间不会释放直到进程退出。df能看到空间一直被占着du却找不到任何对应的目录项。挂载点“遮盖”了下一层目录比如/data下面挂载了一块独立磁盘/data本身在根分区里有个空目录你用du -sh /统计根目录时如果不用-x参数它会穿过挂载点把新磁盘上的内容也算进去反过来如果你只统计/的某个子目录可能又会漏掉挂载点上实际占用的空间。理解了这些原理后面排查问题的时候思路就会清晰很多。du和df不是矛盾的而是互补的一个管微观、一个管宏观。2.du命令的实战用法把每个文件夹的真实占用翻出来2.1 最常用的几条命令模板du的参数不少但日常用到的核心就那么几个。我个人最常用的命令是下面这组# 统计当前目录下所有一级子目录的大小输出人类可读格式 du -h --max-depth1 ./ # 只看当前目录本身的总体大小 du -sh ./ # 统计当前目录下所有子目录的大小并按大小倒序排列 du -h --max-depth1 ./ | sort -hr # 统计指定目录下每个子目录的大小并排除某些目录 du -h --max-depth1 /var/log --exclude*.gz-h是--human-readable把字节数换算成K、M、G这样的单位-s是--summarize只显示总计--max-depth1用来控制递归的层数这个参数比-d 1写起来更直观两个作用一样随你习惯用哪个。sort -hr里的-h是让sort能正确比较带单位的数字比如2G和200M谁大sort默认按字典序是分不清的-r是倒序这样最大的目录排在最上面。2.2-x参数别跨挂载点避免统计陷阱这个参数在排查磁盘占用时极其重要。-x的全称是--one-file-system意思是“不要跨越文件系统边界”。这么说可能有点抽象我举个例子。假设你的根分区/已经使用了95%你想找出根分区里什么东西最大。如果你直接跑du -h --max-depth1 /它会把你挂载在/home、/data、/opt等位置的其他磁盘内容也一起统计进去。这会导致你花半天找到一个“巨大”的目录结果发现它其实在另一块独立的磁盘上和根分区空间告急半毛钱关系都没有。正确的做法是加上-xdu -x -h --max-depth1 / | sort -hr这样du在遍历到挂载点边界时就会停住只统计当前文件系统内的内容。这个参数在排查“某个分区满了但我不知道谁占的”这类问题时能帮你省下大量无效操作。2.3 参数组合的讲究什么时候用-h什么时候用-k或-m-h虽然直观但在脚本里处理时反而麻烦因为sort、awk这些工具解析带单位的字符串不太方便。如果你要写脚本统计目录大小并做条件判断我建议用固定单位# 以KB为单位方便脚本计算 du -k --max-depth1 /var/log | sort -rn # 以MB为单位 du -m --max-depth1 /var/log | sort -rndu -k和du -m的输出是纯数字第一列是大小KB或MB第二列是目录名写shell或Python脚本处理起来非常顺手。另外注意du -m在Linux上表示1024KB的MB不是1000KB的MB和硬盘厂商的换算标准不同别搞混。2.4 实战案例找出当前目录下最大的文件夹我把这个场景完整跑一遍演示从执行到分析再到清理的整个过程。假设当前在/home/user最近发现这个目录占了大量空间。cd /home/user du -h --max-depth1 ./ | sort -hr输出大概是这样的4.2G ./.cache 2.1G ./anaconda3 980M ./node_modules 720M ./Downloads 340M ./.local 120M ./.config ...一眼看过去.cache是最大的接着是Anaconda的安装目录。接下来继续往下钻du -h --max-depth2 ./.cache | sort -hr然后一层层追下去直到找到具体的元凶文件。整个过程就像剥洋葱从最大的目录开始逐层细化。这里有个经验优先清理.cache下的pip缓存、~/.cache/thumbnails缩略图缓存、以及各种软件留下的缓存文件这些通常是安全的清理对象删了也不会影响软件正常运行最多下次启动时重新生成。3.df命令的深度使用掌握磁盘剩余空间的全局视图3.1df基础的三个用法df的日常用法比du简单得多但我还是要强调几个容易被忽略的点。最基本的用法# 查看所有挂载点的空间使用情况人类可读格式 df -h # 只看某个挂载点或目录对应的文件系统 df -h /home # 查看所有文件系统包括0空间的伪文件系统tmpfs、devtmpfs等 df -adf -h是绝大多数人用到的第一条命令。它输出六列文件系统Filesystem、总大小Size、已用Used、可用Avail、使用率Use%、挂载点Mounted on。注意那个Avail列在ext4文件系统里默认会预留5%给root用户所以普通用户看到的可用空间会比实际少。如果你的磁盘是给数据库这类服务用的保留这5%是好事关键时刻能救你如果是普通数据盘想把这部分空间释放出来给普通用户用可以执行tune2fs -m 0 /dev/sdX1把预留比例改成0。3.2 inode耗尽另一个“空间满”的隐藏原因很多人以为磁盘满就是空间不够其实还有另一种“满”——inode耗尽。inode是用来存储文件元数据权限、所有者、时间戳、数据块指针等的索引节点每个文件或目录都要占用一个inode。当分区里的inode用完了即使磁盘还剩下大量空间你也无法创建任何新文件或目录各种服务会报“No space left on device”。查看inode使用情况很简单df -i输出格式和df -h几乎一样只是把Size/Used/Avail换成了Inodes/IUsed/IFree。如果某个分区的IUse%接近100%那你就要检查是不是有大量小文件堆积。典型的元凶包括邮件队列/var/spool/mqueue、squid缓存、临时目录里几百万个小文件、容器日志文件等。排查inode占用大户可以用# 统计当前目录下每个子目录的文件数 find . -xdev -type f | cut -d/ -f2 | sort | uniq -c | sort -rn这个命令把当前目录下所有文件路径的第二段也就是一级子目录名提取出来统计每个子目录下的文件数量倒序排列。找到文件数最多的目录后再进去重复执行直到定位到具体问题。3.3 关于df读取的是“缓存的值”这个细节df展示的数据来自文件系统的元数据这些值是系统在挂载时读取并缓存的平时更新频率并不高。所以有时候你明明删除了大批文件立刻执行df -h却发现数值纹丝不动。这是正常的文件系统会在合适时机把缓存里的变化同步到超级块。如果确实需要强制执行同步、刷新统计信息可以执行sync命令。一般情况下没必要让它自然更新即可但在脚本里判断“删除后是否释放了空间”时最好留出几秒钟的缓冲。4. 磁盘占用排查实战从告警到定位的全流程4.1 场景一根分区/使用率接近100%的快速定位这是运维和日常使用中最常见的故障场景之一。系统突然各种服务异常、写入失败、shell无法创建临时文件一看df -h发现根分区Use%已经满了。这时候按下面这个流程排查一般几分钟就能定位到问题。第一步确认根分区的文件系统边界并统计各目录占用df -h / du -x -h --max-depth1 / 2/dev/null | sort -hr2/dev/null是把权限不足的报错信息扔掉因为根目录下很多系统目录普通用户根本无权访问但统计时还是希望看到所有目录的大小。如果是在普通用户下执行这一步需要看具体情况权限不足的目录会被跳过如果要完整统计可以加sudo。第二步找到最大目录后继续向下钻取du -x -h --max-depth1 /var 2/dev/null | sort -hr第三步持续钻取直到定位到具体文件。这里有个经验技巧只要看每个层级最大的那个目录就够了不要平均用力。另一种灵活的思路是用find直接找出超过一定大小的“大文件”find / -xdev -type f -size 100M -exec ls -lh {} \; 2/dev/null这个命令在根文件系统范围内找出所有超过100MB的普通文件并列出大小和路径。在空间紧急的情况下这个命令往往比逐层du更快它直接把大头文件揪出来。缺点是你只能看到单个大文件看不到大量小文件累积出来的大目录。4.2 场景二日志目录的专项清理日志文件是磁盘空间杀手排行榜的常客。/var/log下面是各种服务日志的聚集地journald的日志尤其容易膨胀。如果你用的系统带systemdjournal日志通常默认占用系统日志分区的最大比例可以通过下面命令查看journalctl --disk-usage如果发现journal占了好几个G可以设置它的大小上限和清理策略# 限制journal最大占用500M journalctl --vacuum-size500M或者在配置文件/etc/systemd/journald.conf里修改SystemMaxUse500M让它在未来持续控制上限不会无限增长。这个配置改完要重启systemd-journald服务才生效systemctl restart systemd-journald对于传统的文本日志比如Tomcat、Nginx的access.log它们通常会按天或按大小轮转。如果你发现某个日志文件单个就有好几个G先确认是不是轮转配置失效再手动清理。手动清日志时不要直接rm掉让进程继续守着旧文件描述符也不要简单用 file清空却忘了去管正在写的进程正确做法是先truncate -s 0 /path/to/log把文件截断成0字节进程继续写入时不会出问题空间也立刻释放了。4.3 场景三定时巡检脚本的编写思路与其等到磁盘满了再手忙脚乱不如写个简单的巡检脚本定期检查。我的做法是写一个shell脚本放在cron里每天执行一次当某个分区的使用率超过阈值时发告警邮件、企业微信机器人、钉钉机器人看现场情况。脚本核心逻辑很简单#!/bin/bash THRESHOLD85 df -h | awk NR1 $5 $THRESHOLD {print $0}这里awk的$5是把第五列Use%里的百分号字符串转成数字然后和阈值比较超过85%就输出该行。实际生产环境里可以直接把打印出来的行交给通知脚本处理。如果你还想顺便把增长最快的目录找出来可以在脚本里加一段du统计把结果存到历史文件里用diff观察哪些目录增长最快。这个思路在排查“磁盘总是不明原因被占满”的慢性问题时非常有效。4.4 场景四被删除仍被占用的文件du和df结果不一致的经典场景这个场景我单独拎出来说因为它是du和df统计差异最大、也最让人抓狂的情况。现象是df -h显示/用了90%但du -x -h --max-depth1 /把根目录所有内容加起来也就占50%。中间凭空消失的40%哪去了大概率是某个进程打开了一个大文件然后这个文件被rm了但进程没退出句柄还开着。排查方法lsof L1这个命令列出所有被删除但仍被进程打开的文件。L1表示只显示link count小于1的也就是已经被删除的文件。输出结果里你会看到类似(deleted)标记的文件路径和对应PID。确认是哪个进程后处理方式有两种一是重启这个进程让文件描述符释放二是如果不方便重启就看情况决定是否等待比如滚动日志有时会定期重新打开。还有一个思路是用ls -l /proc/PID/fd/查看进程打开的文件描述符定位到具体是被哪个文件占用的。这个方法在lsof没装的时候也能用。5. 我踩过的坑和几个独家技巧5.1 坑一不用--exclude导致统计结果失真du默认会遍历目录下的所有内容包括那些你根本不想统计的挂载点、虚拟文件系统、其他目录的软链接指向的内容。比如/proc、/sys、/dev这种虚拟文件系统里的内容du去统计纯属浪费时间数值也没意义。实际使用中建议加上排除du -x -h --max-depth1 / --exclude{/proc,/sys,/dev,/run} | sort -hr--exclude支持glob模式排除多个目录时用大括号括起来。在我实际的项目中排查容器宿主机磁盘占用时这个技巧让我避开了/var/lib/docker/overlay2和/var/lib/docker/containers这类容器根基目录的干扰能更快定位到业务数据的位置。5.2 坑二文件系统配额和预留空间的“隐藏”占用如果你用df看到一个分区UsedAvail不等于Size中间缺了一截不用太惊讶。这缺掉的部分大概率是文件系统预留块默认5%或者你设置了配额quota但df不直接显示配额限制。ext4、xfs这类文件系统都支持预留空间机制目的是给root用户留出紧急恢复的余地以及减少文件碎片。所以管理磁盘容量时要把这个“隐藏”的5%算进去特别是规划容量的时候别按100%去规划按95%或者90%去算才靠谱。5.3 技巧一用ncdu交互式查看目录占用du虽然强大但输出是一大堆文字快速浏览体验一般。如果你在服务器上能装软件我非常推荐ncduNCurses Disk Usage。它的界面是交互式的上下左右浏览目录按d删除文件按n按文件名排序按s按大小排序用起来比反复敲du命令高效很多。安装方式很直接# Debian/Ubuntu apt install ncdu # CentOS/RHEL yum install ncdu我第一次用它排查一个几TB的存储服务器时不到一分钟就定位到了占用异常的目录。如果你经常处理磁盘空间问题ncdu值得加入常用工具箱。5.4 技巧二在脚本里处理du和df结果时的单位陷阱前面提过du -h输出的是4.2G这种带单位的字符串直接做算术判断会出问题。写脚本时我建议使用固定单位比如du -kKB或du -mMB。awk处理时直接比较数字即可。而df -h也是一样的问题脚本里我更推荐用df -PPOSIX输出格式单行输出避免路径过长换行配合awk做判断。-P这个参数很重要因为默认情况下df遇到超长路径会换行输出awk解析时会错位-P强制不换行脚本才能稳定工作。5.5 技巧三结合watch命令实时监控磁盘变化在清理磁盘空间时我习惯在另一个终端开着watch -n 1 df -h每秒刷新一次磁盘使用情况。这样我在另一个终端删文件时能立刻看到空间是否真的被释放了。特别是处理那些“删了文件但空间没变”的情况时实时观察特别能说明问题。比如你删了一个大文件如果df数值纹丝不动说明大概率有进程还占着这个文件如果数值缓慢下降那可能只是文件系统在后台同步。另外清理过程中如果发现某个目录反复出现异常增长可以用watch -n 60 du -sh /var/log监控特定目录的增速判断是正常写入还是异常增长。结尾最后再分享一个小习惯。每次排查完磁盘空间问题我不只是把空间清理掉就结束了还会顺手记录一下“这次是哪个目录/文件膨胀了、为什么膨胀、怎么预防”。时间久了你会发现自己对服务器上什么东西在持续增长、什么时间点会出现空间尖峰了如指掌。这套“du定位目录、df看全局、lsof揪隐身占用、脚本定巡检、ncdu做交互排查”的组合拳打下来磁盘空间问题基本不会让你手足无措。输入du和df这两个命令只需要一秒钟但真正用好的价值是帮你在一堆数据里快速精准地找到问题所在而不是靠瞎猜乱试。希望这篇文章能把这两个命令在你这儿从“会用”变成“用好”。