ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Linux磁盘占用排查实战:从du基础参数到生产环境踩坑

Linux磁盘占用排查实战:从du基础参数到生产环境踩坑 磁盘空间告急的时候Linux 下第一个想到的命令是什么我大概率会先敲df -h看整体再用du定位到底是哪个目录在偷吃空间。du这个命令看起来简单但真正用好的人其实不多。很多人只会du -sh遇到目录一多、文件一杂就开始抓瞎要么输出的信息看不懂要么统计出来的数字莫名其妙对不上。这篇就围绕du命令把磁盘占用这件事讲透从基础参数到实战排查再到踩坑经验一次说清楚。适合刚接触 Linux 的新人也适合时不时要处理磁盘告警的运维和开发。1. 整体设计思路du 到底解决什么问题1.1 磁盘占用分析的核心场景但凡跑过一段时间 Linux你一定会撞上这类场景网站突然打不开一看磁盘 100%日志服务报错df -h显示/根分区只剩几百兆或者说不上来哪里不对就是觉得服务器越来越慢。这时第一反应是“谁把磁盘吃掉了”而du就是干这个用的。dudisk usage能从指定路径开始递归统计每个子目录和文件占用的磁盘块数量进而帮你搞清楚空间消耗的具体分布。它的核心价值在于定位不光知道磁盘满了还知道是/var/log满了还是某个 Docker 容器目录膨胀了或者是某个用户的家目录里藏了一堆大文件。有了精确的定位清理才有方向。1.2 du 和 df 的分工逻辑我见过不少人把du和df混着用甚至以为它们是同一个东西。这俩虽然都是看磁盘但视角不同df看的是文件系统层面统计整个挂载点的总容量、已用空间、可用空间du看的是文件层面逐个文件、逐个目录地累加实际占用的块。还是用生活类比解释一下df就像你站在银行门口看账户总额du则是把每一笔消费流水拉出来告诉你钱具体花在哪。这个区别在实战中非常重要。如果你发现df显示磁盘已经用了 80G但拿du把根目录下所有目录加起来只有 50G不用慌这很正常。差值通常来自三类一是被删除但仍被进程占用的文件二是文件系统元数据三是挂载点目录下的隐藏文件系统。后面我会在排查部分细讲这个问题。1.3 du 的统计原理与设计哲学du的底层实现其实不复杂它遍历目录树对每个文件调用stat拿到st_blocks字段这个字段表示文件占用了多少个 512 字节块然后递归累加。默认情况下du统计的是实际磁盘块占用而不是文件的逻辑大小这解释了为什么du显示的数字经常比ls -l看出来的文件大小要小——稀疏文件、小文件簇、压缩文件系统都会造成差异。搞清楚这个原理你就能理解du一些“反直觉”行为。比如一个文件逻辑上是 4KB但实际只占用了 4KB 的半个块du统计出来的可能只有 2KB又比如硬链接的文件du只会统计一次。这些细节单独看可能觉得无所谓但在精确排查空间问题时往往就是那些“对不上账”的根源。2. 核心细节解析du 关键参数与选型依据2.1 常用参数速查表du命令的参数不少但真正高频使用的就那么几个。我整理了一个速查表按实用程度排序参数作用典型用法-h人类可读格式自动换算为 K/M/Gdu -h-s只显示总计不展开子目录du -sh /var-a同时显示所有文件而不只是目录du -ah /var-c最后额外输出一个总计行du -ch /var/log/*--max-depthN限制递归深度du -h --max-depth1 /-x不跨文件系统只统计当前文件系统du -xh /--excludePATTERN排除匹配模式的文件或目录du -sh --exclude*.log /var-t/--threshold只显示超过指定大小的条目du -ht 100M /var/log--block-sizeSIZE按指定块大小输出du --block-size1M -s /home这里提醒一下新手-s和--max-depth0的效果一样都只输出当前目录的总大小。但-s更简洁日常用顺手了就很难改回来了。2.2 理解统计粒度和输出逻辑用du最容易搞混的一个点是它默认只输出目录信息不输出文件信息。比如你执行du -h /var/log看到的是一行一行的目录及其大小但/var/log/messages这样的具体文件不会单独出现除非你加了-a参数。为什么这么设计因为大多数场景下目录才是我们关注的粒度——你先找到哪个目录大再进去细查比直接输出成千上万个文件更为高效。不过真到了找大文件环节光看目录又不够。这时可以用du -a把所有文件和目录都列出来再结合排序工具把最大的几条揪出来。后面实操部分我会专门讲怎么组合。2.3 性能与精度之间的权衡du处理大目录树时可能会很慢尤其是面对海量小文件的时候。这其实不是du的问题而是它必须逐个stat每个文件这个开销躲不掉。但在实战里有几招可以显著提速第一招是加-x防止du跨文件系统递归。比如你的/下挂着一个大数据盘/data不带-x去扫/du会把整个/data也统计进去既慢又容易造成误解。第二招是尽量从父目录往上查的时候用--max-depth限制深度。比如你怀疑是网站目录出了问题直接du -h --max-depth2 /var/www先粗扫一遍比直接层层深入要快不少。第三招是用--exclude跳过大目录中已知占用很多但不需要统计的内容比如各种镜像缓存、日志备份。这不光能提速还能让输出数据更加聚焦。3. 实操过程与核心环节实现3.1 从零开始先学会看目录大小分布先说最简单的入门组合。假设你现在收到磁盘告警登录服务器后第一件事可以先敲df -h确认是哪个分区满了。假设结果显示/根分区用了 95%接下来用du定位cd / du -h --max-depth1 2/dev/null | sort -rh解释一下这行命令--max-depth1让du只统计根目录下一级子目录的大小sort -rh按人类可读的数字倒序排列。2/dev/null把权限报错信息过滤掉免得满屏Permission denied毕竟有些目录当前用户确实没权限看。我实操时的输出大概是这样的4.2G /var 2.8G /usr 1.3G /home 512M /opt ...一眼就能锁定目标/var最大。但注意这只是一个粗筛/var里面还包含日志、缓存、lib 目录等一堆东西还得继续往下钻du -h --max-depth2 /var 2/dev/null | sort -rh | head -20逐步缩小范围一层层追下去一般几个来回就能找到大户。3.2 反转思路直接找出最大的文件而不是目录很多时候磁盘占大头的是几个孤零零的大文件藏在中小目录里这时候按目录层层钻进效率就有点低了。更直接的方法是用-a把所有文件也列出来再排序取前几名du -ah /var 2/dev/null | sort -rh | head -10-a让文件也出现在结果里sort -rh后head -10拉出占用最大的 10 个条目。假如你主要关心那些超过 1G 的文件还可以加个阈值du -ah /var 2/dev/null | sort -rh | awk $1 ~ /G/ {print} | head -20不过这里想提醒一句sort -rh依赖 GNU 版本的du输出带 K/M/G 后缀才能正常排序如果你在精简的嵌入式系统或者 macOS 上跑-h和排序的行为可能不一样。稳妥做法是让du输出纯数字字节排序之后再自己格式化du -ab /var 2/dev/null | sort -rn | head -10 | awk {printf %.2f MB\t%s\n, $1/1024/1024, $2}-b强制按字节计数先把大文件全找出来再做展示格式化这招在跨环境时特别省心。3.3 排除无关目录聚焦真正要清理的目标真实的生产环境比实验室复杂得多。比如/var/log下有个历史日志目录你知道很大但暂时不能删又比如你的项目目录里有.git统计空间时想单独看业务代码占了多少。这时候--exclude参数就派上用场了du -sh --exclude*.log --exclude.git /home/project注意排除模式支持通配符多个--exclude可以叠加使用。这个参数不仅省事最关键的是能让统计数真正反映你关心的对象。我曾经帮人排查过一个 Java 应用的问题应用目录统计下来 5G排除掉日志之后只剩 800M清理思路一下子就清晰了。3.4 不给du输出那么大压力的技巧有人可能会踩到这样一个坑某个目录文件特别多比如几百万个缓存小文件这时候跑du不仅慢中间还可能被系统 OOM 杀进程。实际上du本身不会把所有文件都装进内存它是在遍历过程中就输出结果真正卡住你的往往是执行sort和head时的管道数据量。更稳的做法是加一个缓冲区或者干脆让du只输出目录层级的统计文件级分析交给findfind /var/cache -type f -size 100M -exec ls -lh {} \; 2/dev/nullfind直接按大小条件扫文件并不需要先列出全量清单再排序在海量小文件的场景下比du -a | sort更高效。从我的经验来看混合使用du看目录总量和find筛单个大文件才是生产环境里最快定位磁盘空间问题的黄金组合。3.5 排查日志目录的完整流程演示下面我把一个真实的排查流程完整走一遍方便你照着做。场景是根分区告警怀疑是日志文件太多。第一步先确认整体空间情况df -h第二步锁定根分区下最大的子目录du -h --max-depth1 / 2/dev/null | sort -rh | head -10第三步如果发现/var/log是罪魁祸首直接查它里面的大文件du -ah /var/log 2/dev/null | sort -rh | head -15第四步分析结果看哪些是历史归档日志、哪些是当前正在写的日志然后决定是轮转、压缩还是删除du -ch /var/log/*.gz 2/dev/null | tail -1第五步清理之后再用df -h复查空间是否释放。这里要特别强调光删文件有时候df显示的空间不会立刻释放原因后面排查部分会专门展开。整个流程走下来大概五六分钟确认、定位、清理、复核一步不缺。4. 常见问题与排查技巧实录4.1 Permission denied 一屏又一屏用du扫根目录时Permission denied几乎是必然出现的。原因很简单普通用户对/root、部分系统目录没有读权限。虽然这不影响最终统计没权限的目录直接跳过但满屏报错会干扰你阅读结果。最常见也最省事的处理方式是du -sh /root 2/dev/null或者把错误输出丢到单独文件里du -sh /root 2error.log需要说明的是如果真的想统计完整系统建议直接切到root用户执行或者用sudo因为只有特权用户才能真正扫到所有目录。这个操作本身没风险只是读取元数据而已。4.2 du 和 df 数字对不上怎么回事这是被问到最多的问题之一df显示已用 200G但du -sh /只统计出 150G那 50G 去哪了最常见的原因是“文件被删除但仍被进程占用”。Linux 中如果一个文件被打开着进程没退出即使你把它从目录里删了文件占用的磁盘块也不会释放直到进程关闭文件或者进程结束。想确认是否存在这种情况可以用lsof L1列出那些被删除但仍被打开的文件然后找到对应 PID重启进程或让进程重新打开文件空间就释放了。另一个常见原因是挂载点目录下有子文件系统du默认会跨文件系统统计看起来数字就对不上。比如你在/mnt/data挂了一块独立磁盘然后执行du -sh /mnt它会把整块数据盘全算进去如果执行du -xsh /mnt则只统计/mnt目录本身当前文件系统上的内容。两种统计逻辑不同自然会有差异。4.3 du 扫了半天没输出以为卡死了目录文件特别多的时候du确实会长时间没有反馈因为它在遍历而你没加-h的话连大小都不显示搞得人心里没底。我建议这种场景下加--progress选项GNU coreutils 支持du --progress -sh /data它会每 N 秒打印当前正在统计的子目录让你知道它到底扫到哪了。如果你的du版本不支持--progress也可以用strace看系统调用确认它还在干活不过生产环境中没必要这么狠耐心等就好。4.4 硬链接文件占用被重复计算其实是特性du对硬链接的处理挺有意思同一个文件如果有两个硬链接指向它du默认只计算一次。这是对的因为磁盘空间确实只被占用了一份。但如果你分别对两个链接所在目录执行du每个目录都会把这份空间算进去单看一个目录会觉得空间变多合起来也会超过整体。这个特性不是 bug理解就好。排查时如果发现统计总和对不上可以看一下是不是硬链接造成的计算重叠。4.5 du 参数在脚本中的坑把du写进自动化脚本时用-h反而容易出问题因为15K、1.2G这种字符串在数值比较时会非常别扭。我自己的习惯是统一用字节数输出脚本处理干净利落SIZE$(du -sb /var/log | cut -f1) if [ $SIZE -gt 1073741824 ]; then echo /var/log 超过 1G需要处理 fi-s汇总、-b按字节、cut -f1取出第一列数字这套组合在脚本里是最稳的搭档。这里顺便提个细节du -sb与du -s --block-size1在部分系统上输出一致但-b更通用。4.6 容器场景下 du 的另类用法现在很多服务器上跑着 Docker 或 Kubernetes磁盘空间经常被容器镜像、日志、挂载卷吃掉。虽然主流分析工具是docker system df但它其实也是基于du的统计逻辑实现的。手工排查时可以在宿主机的容器数据目录一般是/var/lib/docker/overlay2或/var/lib/containerd上用du找大头du -h --max-depth1 /var/lib/docker/overlay2 2/dev/null | sort -rh不过这里容易误导人overlay 目录里同一层文件会被多个容器共享你在宿主机上看到的du统计其实不是“每个容器实际占用”而是若干镜像层和容器层的累加。真要看每个容器的可写层大小更准确的方式还是用容器命令配合查看不要直接在 overlay2 目录上做决策。5. 从经验出发把 du 用得更顺手的小技巧5.1 组合命令一次找出超过 1G 的目录日常巡检时我习惯用一条命令把整个磁盘占用情况拉出来du -h --max-depth1 / 2/dev/null | sort -rh | awk $1 ~ /G/ {print}只看超过 1G 的目录这条命令在绝大多数服务器上都能快速粗筛出可疑目标。本质上这是把du的结果交给sort和awk二次加工三个命令各干各的组合起来却比任何图形化工具都直观。5.2 别让 du 成为一个常驻命令有段时间我图省事用watch -n 60 du -sh /var/log去定时看日志增长情况。次数多了就发现这个方法有利有弊好处是日志增长曲线一清二楚坏处是对 IO 有一点压力和干扰。后来我调整了策略把这个动作改成每天凌晨用计划任务跑一次把结果追加到报告文件里du -sh /var/log /tmp/disk_report.log date /tmp/disk_report.log需要时再回头翻记录既省事又不会打扰在线业务。5.3 善用 alias 和函数如果你和我一样三天两头用du强烈建议在~/.bashrc里加几个别名alias du1du -h --max-depth1 2/dev/null | sort -rh alias ducdu -sh --exclude*.log这样平时敲两三个字母就能完成常用操作。要是你还想要更直观的进展速度对比可以把du1配合headdu1 /var | head -8实际用下来这套配置就是我查磁盘问题的默认入口快、准、省心。5.4 什么时候会用到 xargs 批量操作定位到大目录之后如果你想批量清理符合条件的历史文件du经常和xargs搭档。比如找出所有超过 500M 的.log文件find /var/log -name *.log -size 500M -print0 | xargs -0 du -h先find候选文件再用du汇总实际占用两步分离每一步的结果都可控。你也可以在确认安全之后把这个操作替换成清理动作但在生产环境上我强烈建议先用du看清楚大小和路径别一上来就直接删。5.5 配合 ncdu 获得交互式体验命令行用久了偶尔也会想念图形化界面。ncdu是一个基于文本界面的磁盘分析工具底层相当于对du结果做交互式展示上下键选择目录回车进入d删除q退出。安装很简单多数发行版仓库里都有apt install ncdu或者说yum install ncdu如果你经常需要在大目录树里来回查看精确到每个子目录的分布ncdu能省下大量敲命令的时间。它是du的补充而非替代两个配合着用我认为是排查磁盘占用问题时的最佳体验。写在最后我踩过几次坑之后的体会du命令看着不起眼但它在 Linux 运维中的地位一直很稳。从我个人的经验看真正高效排查磁盘占用的核心不在于背多复杂的参数而是先建立一套清晰的思路先用df确认整体再用du逐层定位目录接着用find揪出单个大文件最后清理并复查。每次按这个流程走基本十分钟就能完成一次磁盘告警的处置。最后再分享一个小细节无论是du还是其他磁盘分析命令输出的数字只是一个参考真正做清理决策前一定要确认文件是否能删、有没有进程还在使用。尤其是日志和服务数据宁可多看一眼进程列表也不要因为一个rm让服务起不来。工具的作用是帮你把问题看清楚而最后的判断永远得靠人。
返回列表