ARTICLE DETAIL

建站实战干货

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

Linux磁盘占用排查:du命令从基础到实战完全指南

2026/10/2 21:50:04 拓冰建站 浏览量
Linux磁盘占用排查:du命令从基础到实战完全指南 今天聊一下 Linux 下最常用的磁盘占用排查命令du。不管你是在生产服务器上看到报警“磁盘空间不足”还是本地开发环境莫名撑爆了根分区总得先搞清楚哪个文件、哪个目录把空间给吃掉了。du 就是干这个的。这个命令的核心能力是递归统计文件和目录的磁盘占用配合排序和排除参数可以快速锁定“到底是谁占了我的硬盘”。它和 df 这种查看分区整体使用率的工具完全不同前者是“从下往上逐一把账目算清”后者是“直接看总额还有多少余额”。这篇内容我会从最基础的用法开始逐步深入到实战排查场景顺带把我在运维过程中踩过的坑、试过的技巧都写出来适合刚接触 Linux 的初学者也适合已经会用但想系统梳理的运维同行。我在实际使用中发现du 这个命令看起来很“简单”但它有一堆容易忽略的细节为什么统计出来的空间大小和 df 对不上为什么删除文件后磁盘空间没有释放为什么小文件明明只有 100 字节却显示占用了 4K这些问题不弄明白排查磁盘问题时很容易走弯路。下面我从头到尾讲清楚。1. 为什么需要 du 以及它和 df 的根本区别很多人最开始分不清 du 和 df以为两个都是看磁盘的命令随便用哪个都行。实际上它们的统计口径完全不同排查思路也因此不一样。1.1 一句话理解 dudu 的全称是 disk usage它的工作机制是进入你指定的目录逐层递归地访问每一个子目录和文件从文件系统读取每个对象的元数据里的块数信息然后一层层累加汇总最后输出该目录树的总占用。你可以把它想象成你想知道家里有多少存款但银行给你的是总额你反而要自己拿个小本子把每张银行卡、每笔理财、抽屉里的现金逐项记下来最后加总。如果你只想知道某个目录装了多大用du -sh /路径一条命令就搞定了。1.2 du 和 df 的差异df 的全称是 disk free它查看的是整个文件系统、整个分区的使用情况比如/dev/sda1挂载在/总共 100G已用 80G剩余 20G。df 不关心具体是哪个目录用了这 80G它只知道整个分区层面还剩多少。du 则相反它深入到目录内部把每个文件占用的实际磁盘块数统计出来最后汇总。所以排查“哪个目录把空间占满了”的正确答案一定是用 du而不是 df。我用一个直观的对比表来说明维度dfdu统计对象文件系统/挂载点目录和文件统计方式读取文件系统超级块信息递归遍历目录树能否定位目录不能只显示整体能逐级列出常用场景确认哪个分区满了确认哪个目录或文件占空间多返回速度快毫秒级慢取决于目录大小1.3 磁盘空间去哪儿了先从系统层面排查如果你接到了磁盘告警我的建议是先用df -h看一遍所有挂载点确认到底是哪个分区满了同时也看一下当前工作目录所在文件系统是否是那个满的分区。这里有个经常出现的误区你在/data下执行du -sh结果发现占用很小但df -h却显示/data所在磁盘已经 100% 满了这种差异往往不是算错了而是有隐藏的占用后面我会专门讲这个问题。2. 基础用法从最简单的 du -sh 开始我平时几乎天天用du -sh因为它的输出最干净只显示汇总大小不打印一大串子目录路径。对于快速判断一个目录的整体体量这是最顺手的方式。2.1 查看当前目录总大小直接在目录里执行du -sh .输出结果可能是1.2G .-s表示 summary只输出总计-h表示 human-readable以 K、M、G 等单位显示。如果你不写-h输出会是一堆以 KB 为单位的数字比如 1258292看起来非常吃力所以我建议在任何地方都习惯性加上-h。2.2 查看指定目录查看特定路径时直接跟上目录名即可du -sh /var/log du -sh /home/user du -sh /var/log /home/user /tmp最后一条命令会一次性输出多个目录各自的占用后面我会结合排序命令找出最大的那个。2.3 单位选择不要被默认的 4K 弄糊涂-h是自动选择合适单位但在脚本或者是需要统一口径时可能需要固定单位-k以 KB 为单位-m以 MB 为单位-B 1以字节为单位--si使用 1000 进制而不是 1024 进制比如 1G 表示 1000M而不是 1024M很多人没注意到-h的“人类可读”并不是标准意义上的 1024 进制GNU du 默认以 1024 为换算单位。如果某些软件或者云平台要求按 1000 进制计算就用--si控制。另外细心的朋友可能会发现一个只有 10 字节的小文件du -h却显示 4.0K。这是因为磁盘按块分配空间默认块大小是 4096 字节一个文件至少要占一个块。所以 du 统计的是“实际占用的磁盘空间”不是文件内容的“逻辑大小”。如果你要看书面的文件大小用ls -l如果要看占了多少磁盘块用du。想单独看逻辑大小时可以加--apparent-size参数du -h --apparent-size somefile这个参数在判断“一堆小文件实际占用”时很有用因为稀疏文件、压缩文件、以及大量小文件场景下逻辑大小和实际占用往往差异巨大。2.4 隐藏文件与通配符细节一个非常容易被忽略的坑是du -sh *这个写法默认不会统计以点号开头的隐藏文件。如果你在某个目录下执行du -sh * | sort -h发现所有输出加起来和du -sh .对不上往往就是隐藏文件被漏掉了。比如用户的.cache、.local、.config这类目录经常在不知不觉间积攒了十几个 G。想包含隐藏文件可以这么写du -sh .[!.]* * 2/dev/null | sort -h或者用更稳妥的方式du -sh ./* .[!.]* 2/dev/null | sort -h这里的.[!.]*会匹配.foo这种以点开头但第二字符不是点的项2/dev/null是为了过滤无权限访问时的错误信息。如果你不想要这种通配符的绕法更推荐直接用下一节讲的--max-depth参数。3. 进阶统计子目录占用与排序了解基础用法后真正的高频场景是一个大目录下有很多子目录我要快速找出谁最大。这就需要控制 du 的递归深度再把结果排序。3.1 用 max-depth 控制层级较新版本的 GNU du 支持--max-depthN只向下统计 N 层目录。我常用的写法是du -h --max-depth1 /home等价于du -h -d 1 /home这条命令会列出 /home 下每一个直接子目录的大小以及 /home 本身的总大小。--max-depth1是最实用的参数它避免了一屏刷不完的尴尬同时又能把问题直接定位到一级目录。如果想要看到两级du -h --max-depth2 /home还是那句建议先用 depth 1锁定嫌疑目录再进入子目录继续 depth 1 排查而不是一次性 depth 3 输出几百行。3.2 对结果排序找出“大户”光有数字还不够得把最大的排在最前面。du 本身不带排序功能我们需要把它和 sort 组合起来du -h --max-depth1 /home | sort -h -r这里关键点是sort -h表示按照人类可读的数字大小排序而不是按字典序。如果不加-h你会看到9G排在100M前面因为字母序里 “9” 大于 “1”。-r表示降序最大的在最前。更稳妥的写法是du -h --max-depth1 /home 2/dev/null | sort -h -r | head -20只保留前 20 行避免输出过多干扰判断。如果你所在环境是老版本 sort不支持-h也可以用du -k --max-depth1 /home | sort -n -r | head -20先统一以 KB 为单位排序看够了再转成人类可读数字不过系统里一般都会支持-h。3.3 用 du 配合 find 处理特定文件有时候你不需要统计整个目录树只需要找出某些特定类型的大文件。比如查找当前目录下所有超过 100M 的日志文件find /var/log -type f -size 100M -exec du -h {} \;这里的find负责筛选大文件du -h再负责输出每个文件的实际磁盘占用。这种组合在排查“某个目录总大小正常但某个单文件异常庞大”时非常有效。还可以配合xargs批量处理find /home -type f -size 500M -print0 | xargs -0 du -h | sort -rh-print0和xargs -0的目的都是为了正确处理文件名里的空格和换行符这是我踩过坑之后养成的习惯。如果文件名中有特殊字符直接find ... | xargs du轻则统计错误重则命令失败或误删文件。3.4 排除日志或缓存目录在实际服务器上总有一些目录我们心里清楚它一定很大暂时又不想看比如/var/log下的历史日志或者某个程序留下的缓存。为了避免它们干扰排查可以用--exclude参数du -h --max-depth1 --exclude/var/log /var或者用通配符排除所有.git目录du -h --max-depth2 --exclude*.git /path/to/project还有一个特别实用的场景备份目录。很多项目在代码目录里有 node_modules、vendor、dist 等巨型目录逐个统计时会刷满屏幕。这时候先排除掉它们更快地看到真正需要手工分析的部分。等到定位完业务数据后再单独去检查被排除的目录。4. 实战场景从“磁盘满”到“清理完成”的完整排查流程讲了这么多参数不如完整走一遍真实场景。假设我接到一个任务某台服务器/分区 100%网站开始报错服务起不来。我需要快速找出是谁把磁盘占满了并安全地做出处理。4.1 场景初始化先说明一下我使用的系统是常见的 Linux 发行版GNU 工具集。为了演练我提前在一个测试目录里制造了“案发现场”一个大日志目录、一个缓存目录、一些隐藏文件还有一个被进程删除但仍然被占用的文件。实际排查时第一步永远是df -h输出大概长这样Filesystem Size Used Avail Use% Mounted on /dev/sda1 98G 98G 3.4M 100% / tmpfs 7.8G 0 7.8G 0% /dev/shm ...确认根分区满了下一步就该用 du 定位具体路径了。4.2 第一轮du 自上而下锁定目录在根目录下执行du -h --max-depth1 -x / 2/dev/null | sort -h -r | head -10注意我加了-x这个参数的意思是统计时不要跨越文件系统边界。因为/proc、/sys、/dev等虚拟文件系统在 du 眼里如果递归下去又慢又没意义还可能统计出和物理磁盘无关的数字。-x会让 du 只统计根文件系统上的目录。输出可能是95G /opt 1.8G /var 12M /etc ...一下子就锁定了/opt是最大的疑点。4.3 第二轮进入目标目录继续细分紧接着du -h --max-depth1 /opt 2/dev/null | sort -h -r | head -20输出92G /opt/application 2.1G /opt/backup 800M /opt/software ...继续向/opt/application内部看du -h --max-depth2 /opt/application 2/dev/null | sort -h -r | head -20最后定位到/opt/application/logs/下的access.log.20240101等历史日志文件总大小 90G。这个逐层缩小的过程就是标准的 du 排查法从根开始每次只往下看一层永远把最大目录作为下一轮的入口。4.4 第三轮细看文件级占用当目录下一层已经全是文件、没有子目录时--max-depth就不好用了可以用-a参数列出所有文件du -ah /opt/application/logs 2/dev/null | sort -h -r | head -10这里-a表示列出目录树中每个文件的占用而不只是目录汇总。输出里单个文件的大小一目了然直接就能判断哪个日志该归档哪个缓存该清。4.5 清理与验证清理前强烈建议先和业务方确认哪些文件可以删除。如果只是一些滚动日志常见的做法是压缩归档或者用truncate清空而不是直接rm。比如truncate -s 0 /opt/application/logs/access.log.20240101清空文件之后再执行df -h /确认可用空间回升。如果还有报警继续用 du 下一层找。4.6 补充被删除但未被释放的文件这是排查磁盘空间时最反直觉的事你用 du 统计某个目录发现占用不大但df -h还是满的。这时候十有八九是某个进程打开了文件文件被rm删除但进程还持有该文件描述符所以文件的数据块并没有真正释放。排查方法不是用 du而是用 lsoflsof L1或者lsof / | grep deleted这个命令能列出已经被删除、但仍然被进程打开的文件路径和 PID。看到结果后和进程负责人确认是否可以重启该服务或重载配置。服务重启后文件描述符释放磁盘空间才会真正回来。很多新手在这个问题上卡上一整天其实原因就这么简单。5. 常见问题与避坑清单把平时遇到的典型问题整理成一张速查表方便翻阅现象可能原因解决方法du -sh 和 df 对不上目录中统计时跳过了挂载点/虚拟文件系统或有文件被删除但进程未释放加上-x再用 lsof 排查L1小文件显示 4K、8K磁盘块大小文件至少要占一个块用--apparent-size查看逻辑大小目录统计很慢子目录太多、文件太多或跨越网络文件系统增加 exclude、限制深度、用-x去掉 . 后总和小于总量隐藏文件被通配符漏掉了使用[-a]*或--max-depth权限报错 Permission denied当前用户无权读取部分目录加 sudo或用 2/dev/null 忽略5.1 为什么 du -sh 显示的比 df 用的空间小这是出现频率最高的问题。核心原因是在同一个分区上可能有文件系统预留空间、已删除但未释放的文件、以及 du 默认跳过挂载点。为了排查我会习惯性地用du -x -sh /和df -h /对比。如果仍然差很多重点看 lsof 的输出。我遇到过的最夸张的一次是根分区显示满但整个根目录 du 加起来只有 20G最后发现是某个服务删除了两个 30G 的日志文件进程一直没重启空间一直没释放。5.2 大目录统计耗时怎么办统计几千个文件还好遇到几百万个文件的目录时du 可能要跑几分钟。这时候有几个技巧一是使用ionice降低磁盘 IO 优先级避免影响线上业务ionice -c 2 -n 7 du -sh /data二是可以先把统计放到后台运行nohup du -h --max-depth1 /data du_result.txt 21 等它跑完再看结果期间不需要阻塞终端。三是如果只是日常监控不要频繁对整个根目录跑深度统计建议用定时任务把统计结果定期输出到文件需要排查时直接翻记录。5.3 权限不足的处理普通用户执行 du 时经常看到du: cannot read directory /root: Permission denied这不会导致命令中断只是跳过无权访问的目录最终结果会比实际偏小。如果确实需要完整统计就加 sudo 执行sudo du -sh /root如果不想被错误信息刷屏可以在命令后面加2/dev/null。但在排查阶段我建议不要完全屏蔽 stderr因为你可能需要分辨哪些目录是因为权限无法访问以免漏掉真正的“大户”。5.4 软链接和硬链接的计算差异du 默认不会跟随符号链接展开统计也就是说如果你在一个目录下建了软链接指向/opt然后在当前目录执行du -sh这个软链接本身只占一点点空间不会去统计/opt的内容。这是合理的设计否则一个软链接可能导致重复计算和遍历环。需要强制跟随的时候用-Ldu -h -L /path/to/symlink但生产环境里我基本不用-L容易因循环链接导致结果重复或变慢。硬链接则不同。多个硬链接指向同一个文件磁盘空间只占一份。du 对硬链接的处理是默认情况下同一个 inode 只统计一次如果你用了-l参数才会把所有链接都分别计入。所以在普通场景下du 是符合直觉的“实际磁盘占用”口径。5.5 结果中的 . 表示什么执行du -h --max-depth1时最后一行往往是一个点1.2G /home 12M /home/user 1.2G .这个点在 du 输出里代表当前统计的根目录自身也就是整个目录树的总大小。排序时如果不小心可能把这一行混进子目录里。如果你只想看子目录可以过滤掉它du -h --max-depth1 /home 2/dev/null | grep -v /home$不过在实战中我通常保留这一行因为直接就能看到总占用方便和 df 做对比。5.6 du 与 inode 耗尽的关系最后补充一个很多人会混淆的知识点磁盘空间满有两种情况一种是容量满另一种是 inode 满。du 只能看容量看不出来 inode 是否耗尽。如果你的服务器明明df -h显示还有几十 G但创建文件时却提示“No space left on device”那很可能是 inode 不够了。这时候应该用df -i /查看 inode 使用率。如果 inode 使用率达到 100%即使磁盘容量还有很多也无法新建文件。对于大量小文件堆积的目录尤其是缓存目录、消息队列积压目录经常会出现这种问题。排查 inode 大户可以用find / -xdev -printf %h\n 2/dev/null | sort | uniq -c | sort -rn | head -20这条命令会统计每个目录下文件数量最多的前 20 个位置。这里面的-xdev和 du 的-x一个思路只检查当前分区避免遍历挂载点。我在实际工作中已经养成了一个习惯每次清理磁盘前一定先记录当前的 du 明细到临时文件清理后再次对比确认空间恢复到了预期。这样既方便事后审计也能避免误删导致的服务异常。du 这个命令看似简单但把它的参数吃透、把统计口径理解清楚排查磁盘问题就能少走很多弯路。