尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux磁盘空间管理实战:用du命令精准定位空间占用

Linux磁盘空间管理实战:用du命令精准定位空间占用 在Linux服务器上待久了一定会遇到磁盘被塞满的尴尬。登录不上、服务报错、日志写不进去一查df -h好家伙/分区直接100%。这时候你需要的不是df而是du——它是Linux下做磁盘空间管理最趁手的工具能精确告诉你“每个目录到底吃了多少字节”帮你在几分钟内锁定罪魁祸首。这篇文章不讲花架子直接从实战出发把du的常用参数、典型场景、踩坑记录都摊开讲适合刚入门的新手也适合被磁盘问题折磨过多次的运维老手。1. 磁盘空间管理为什么要选du1.1 du 与 df 的分工很多人一开始容易把df和du搞混其实他俩是互补关系。dfdisk free站在文件系统层面看整体使用率它告诉你“这块盘还剩多少、用了多少”但没法告诉你“具体是哪个目录把空间吃掉了”。而dudisk usage恰好补上这个缺口它从目录树底层往上逐层统计每个文件、每个目录占用的实际块大小是定位大文件和大目录的利器。举个生活化例子df就像小区物业的水表只告诉你这栋楼这个月总用水量du则像每家每户的水表能查出到底是301室还是502室在疯狂用水。所以实际操作中我惯用的顺序是先用df确认哪个分区爆了再用du深入该分区的挂载点一层层往下揪出大目录。两者配合磁盘空间管理才算闭环。1.2 du 的核心思路统计文件占用的块理解du之前先要知道文件在磁盘上的存储方式。Linux文件系统如ext4、xfs按块block分配空间一个块通常是4KB4096字节。哪怕一个文件只有一个字节它也会占用一个完整的数据块加上文件系统自身记录的元数据inode、目录项等实际占用空间往往大于文件逻辑大小。du统计的就是这种“实际占用的物理块大小”也就是你df里看到空间减少的原因。这也解释了为什么用ls -l看到的文件大小和du输出的占用空间经常不一致——前者是逻辑长度后者是与空间占用直接相关的块数。理解这一层后面遇到的很多“诡异现象”就能看得通了。du的默认输出以512字节或1K块为单位不同发行版略有差异稍后会讲如何用-h转成人类可读格式避免每次都要心算。2. du 命令的基础用法与参数拆解2.1 最简用法与输出解读不加任何参数直接跑du它会从当前目录开始递归统计所有子目录的占用并逐行输出。你看到的输出长这样$ du 4 ./.cache 16 ./config 840 .每一行最前面是目录实际占用的块数默认单位是1024字节也就是1K。最后一行的.表示当前目录总占用。这个默认输出在真实环境里很乱——目录一多刷屏刷到你怀疑人生。所以日常我用得最多的组合是du -sh小写h表示human-readable人类可读小写s表示summarize汇总合起来就是“只显示当前目录总大小不要逐个子目录刷屏”。这个命令几乎是我在每台服务器上都刻进肌肉记忆的第一条。$ du -sh /var/log 1.2G /var/log2.2 高频参数详解光会-sh肯定不够排查问题时还得会用下面这几个参数参数作用典型场景-h以K、M、G等易读单位显示du -sh *-s只汇总显示总计不列出子目录du -sh /opt-a同时统计文件而不仅是目录找出特定大小的文件-d N递归深度限制为N层du -h --max-depth1--exclude排除匹配的文件或目录统计时跳过备份目录--time显示目录内文件最近修改时间排查旧日志其中-d或等价的--max-depth尤其好用。比如你想看/根目录下第一层目录各占多少空间执行du -h -d 1 /输出会精简贴近需求每个一级目录一行浅显直观。如果直接跑du -h /那会把每个子目录的孙子目录全打印出来输出量爆炸不说对排查毫无帮助。还有一个容易被忽略的参数是-x--one-file-system。它告诉du不要跨文件系统统计。什么意思如果你的/home是独立分区挂载的不带-x时du -sh /会把/home分区的空间也一并算进去因为它是根目录下的子目录导致总量虚高。加了-xdu就只统计根分区自己的文件不会再跑到别的挂载点上数一遍。这点在有多块磁盘或做NFS挂载的服务器上特别重要稍后坑点章节再详细展开。2.3 可读性与单位换算du默认按1024字节的块数输出不带单位数字大了很难一眼看出大小。加了-h后输出会自动变为4.0K、12M、2.3G这种格式。注意这里的单位进制Linux习惯使用1024进制所以1G其实是1024M而非1000M和硬盘厂商的1000进制不是一回事。这也是为什么你用df -h看到的容量和硬盘标称容量对不上号别慌单位进制不同而已。除了-h还有--si参数可以改成1000进制输出。99%的日常使用不需要关心这个差别但如果你写脚本比较数值就一定要统一单位。我的建议是做人肉排查时用-h写自动化脚本时一律改用-k强制以KB为单位或-mMB这样结果稳定不会因为可读格式解析出问题。3. 实操三个真实场景带你玩转du3.1 场景一排查磁盘被谁占满假设你在凌晨被运维告警吵醒/dev/vda1使用率98%。正常操作是这样df -h du -x -h -d 1 / 2/dev/null | sort -rh | head -20第一行df -h确认是根分区还是其他分区满了。第二行是关键-x防止跨文件系统重复统计-d 1只看根目录下一级子目录sort -rh按人类可读大小倒序排序-r倒序、-h按人类可读数字比较注意这里的r和h顺序不能错head -20只取最大的20个。这样几分钟内就能定位到/var、/home还是/opt在膨胀。定位到具体目录后继续下钻。比如发现是/var问题就执行du -h -d 1 /var 2/dev/null | sort -rh | head逐层往里走最终会找到一个具体目录比如/var/log/nginx然后再配合ls -lht或find找具体文件。这套“层层下钻”的思路本质是不断缩小排查范围我接手过的带病服务器几乎全是用这一套流程救活的效率远高于在/底下盲目跑du -h然后人肉翻屏。3.2 场景二找出目录下最大的文件有时候不是目录多而是单个文件巨大。比如某个应用把日志写进了/tmp/app.log一下子涨到20G。要找大文件可以在目标目录下执行du -ah /data 2/dev/null | sort -rh | head -20-a参数会让du把所有文件也列出来而不只是目录。配合排序和head最大的一批文件直接浮出水面。注意du -a的输出量很大建议指定具体目录别一上来就扫/否则跑个半天不说内容还多到找不到重点。如果你只想看大于某个阈值的文件du自身没有--size过滤功能得配合find实现find /data -type f -size 1G -exec du -h {} \; | sort -rh这条命令先让find筛出大于1G的普通文件再对每个文件执行du -h。我实测过对于几十万小文件的目录find -size 1G -exec du -h {}的组合比纯du -a快得多因为du -a需要把每个文件的尺寸都算出来而find可以利用文件系统的元数据做粗筛。3.3 场景三脚本化统计并输出排序人肉排查适合偶发问题但如果公司服务器多我建议把统计流程脚本化。下面这段是我常用的健壮版脚本适合crontab定时执行并把结果发到日志#!/bin/bash # 磁盘空间统计脚本scan_disk_usage.sh TARGET/var OUTPUT/tmp/disk_report_$(date %F).txt { echo Disk usage report for $TARGET echo Generated at: $(date %Y-%m-%d %H:%M:%S) du -x -h -d 1 $TARGET 2/dev/null | sort -rh | head -20 echo Top 10 largest files find $TARGET -type f -size 500M -exec du -h {} \; 2/dev/null | sort -rh | head -10 } $OUTPUT echo Report saved to $OUTPUT脚本核心还是du但我额外做了三件事第一所有du输出后面加了2/dev/null把权限拒绝等错误信息丢掉避免脏数据第二输出重定向到带日期的文件方便留痕对比第三把目录统计和大文件统计分开做避免互相干扰。把这个脚本丢进crontab每天凌晨跑一次磁盘告警很多都能提前发现而不是等爆了才处理。4. 常见坑与排查经验4.1 权限不足导致的统计偏差在没有权限的目录上跑du它默认会给出一条类似du: cannot read directory /root: Permission denied的警告并且这个目录下面所有内容都不会被统计进去。这会造成统计结果偏低。比如某个进程以root身份在跑产生的文件放在/root下而你用普通用户执行du -sh /看到的结果可能“意外地小”。别高兴太早这只是权限遮住了真相。我的处理方式是有两种一是明确知道需要统计完整时尽量用root账号或者sudo执行du二是将错误输出重定向到单独文件观察哪些目录没被统计。比如sudo du -x -h -d 1 / 2/tmp/du_errors.log | sort -rh | head -20排查完顺手cat /tmp/du_errors.log看看有没有重要目录被挡在外面。这比单纯用2/dev/null吞掉报错要靠谱得多因为吞掉报错后你根本不知道哪些数据缺失了。4.2 硬链接与重复计数的迷惑硬链接是Linux文件系统里一个容易让人困惑的机制。两个不同目录下的文件名如果指向同一个inode它们其实是同一个文件。但du默认对每个目录分别统计导致同一个文件在两个路径里各被算一次最终总占用虚高。典型场景是backup类工具如rsync带--link-dest会创建大量硬链接文件。你用du -sh分别看两个备份目录每个都显示10G总共20G实际物理占用可能只有10G。解决方案是加-l参数吗不对-l是让du把硬链接按多个文件计数默认它会自动识别硬链接并只计一次但目录树不同情况不同。实际排查中如果不确定有没有硬链接干扰可以先用stat查看文件inode编号再用find / -inum 12345找出同一inode的所有路径。如果确认是硬链接导致虚高别担心清理其中一个路径不影响另一个。关键是心里有数别误判“磁盘被重复占用了”。4.3 稀疏文件与空洞文件有些程序会创建稀疏文件sparse file典型代表是数据库临时文件、虚拟机磁盘镜像。稀疏文件的特点是逻辑大小很大但实际分配的物理块很少因为中间全是空洞全是0。比如一个容器镜像文件ls -lh显示100Gdu -h可能只有2G就是这个原理。ls和du在这里差距巨大不代表系统出错。如果你想看宽松占用记住du才是物理真相。反过来如果你用cp去复制一个稀疏文件标准cp会把它还原成实际大小瞬间撑爆目标盘。这时要用cp --sparsealways保持稀疏属性。这个坑我踩过不止一次复制大文件前一定先用du看一眼真实占用。4.4 du 与 ls 显示大小不一致接上面话题du与ls -l对同一个文件显示大小不同原因主要有三个一是块大小对齐前面说过小文件按块分配ls是逻辑大小du是实际占用块二是稀疏文件三是文件系统压缩或快照功能影响如btrfs、ZFS。遇到用户拿着ls -l1.5G和du12K的对比来问“是不是bug”时别解释半天直接告诉他这个文件可能是稀疏文件或空文件。验证办法很简单ls -lh file du -h file stat file | grep -E Size|Blocksstat里的Blocks字段才是真正分配的数据块数量乘以块大小就是du的结果。掌握这个验证链以后再遇到大小不一致的疑案都能轻松释疑。4.5 挂载点与跨文件系统的陷阱服务器磁盘规划复杂时根目录下会挂着很多其他分区。比如/data是独立的一块2T数据盘/backup是NFS远程挂载。此时你在根目录执行du -sh /会把/data和/backup的数据也算进来看着像一个分区占用超大实际却是好几个设备的效果。多数情况下统计单分区使用量都建议加-x参数。这里有个补充经验du -x只统计当前文件系统但并不会自动跳过“挂载点本身入口”的目录元数据所以根目录下的挂载点仍然会显示一个很小的数值这个正常。如果需要列出哪些目录是独立挂载点用mount或findmnt查看比纠结du的输出更有用。还有一点du在NFS挂载目录上跑会非常慢因为要遍历远程文件系统每一次stat都是一次网络往返。遇到NFS目录我建议直接用远程端的工具统计或者等业务低峰期再跑别在白天高峰对着NFS执行du -sh /mnt/nfs卡到怀疑人生。5. 进阶技巧与自动化5.1 结合 find 清理历史日志大日志文件往往是磁盘爆满的“元凶”。用du找到大目录后删除前建议先确认文件是否可删。我常用的安全删除流程是# 找到 /var/log 下超过 200M 的日志文件 find /var/log -type f -size 200M -exec ls -lh {} \; # 确认后先清空而不是直接 rm给进程留个活口 : /var/log/app.log直接rm掉一个正在被进程写入的日志文件不会立即释放磁盘空间因为进程还握着它的文件句柄空间要等进程关闭或重启后才释放。此时磁盘还是满的你会陷入“明明删了文件磁盘却没变”的困惑。正确姿势是先用du -sh *定位再利用lsof | grep deleted找出被删但未释放的句柄最后用 文件清空内容或重启对应服务。这个组合拳帮我解决过无数次“磁盘满但不知道删什么”的奇怪Case。5.2 定时任务自动发送报告前面写了统计脚本配合crontab可以做成告警提示。下面是一个简单范例# 每天凌晨2点执行磁盘统计输出保存 0 2 * * * /opt/scripts/scan_disk_usage.sh /dev/null 21 # 每周一9点检查/分区使用率超过85%发邮件提醒 0 9 * * 1 awk $NF/{if($5085) system(echo Disk usage high: $5 | mail -s \Disk Alert\ adminexample.com)} (df -h)du是主力工具df来辅助判断阈值。实际生产中我不会只依赖一个工具而是把du和df的结果合并成一个报表既看整体水位又看明细分布。脚本输出格式建议使用CSV方便后续导入监控系统du -x -d 1 / 2/dev/null | awk {print $2,$1} /tmp/disk_usage.csv这样后续无论是画图还是入库都有结构化数据可用。5.3 用 du 判断 inode 与目录深度磁盘满不全是块空间问题还有inode耗尽的情况。df -i可以看到inode使用率。当磁盘空间还有但inode满时文件无法创建症状和磁盘满类似。此时du并不能直接看inode数量但它能帮你找出哪个目录下小文件最多。比较笨但有效的办法是在目标目录下递归数文件数量find /data -type f | wc -l配合du的目录大小分布基本能判断出是小文件泛滥还是大文件撑爆。我遇到过一种极端情形某个缓存目录里生成了上千万个4K大小的烤面包屑文件du -sh看目录可能只有几个G但find | wc -l一数几千万inode直接被吃干抹净。这时候用du逐个目录扫找到那个增长最快的目录再结合业务日志清理旧缓存才真正解决问题。du在这类场景的价值是“帮我把注意力集中到异常成长的目录”至于怎么清理那是另外的工具和策略。还有一个经验是配合--max-depth控制递归深度避免误入超级深层的目录结构。比如du -h -d 0等价于du -sh只统计当前目录本身du -h -d 2统计两层以内的目录。深度太大时某些应用生成的目录树有几十层嵌套全量du输出会非常恐怖限制深度再配合sort能快速找到“树中最粗的那根枝干”。6. 关于个人经验的一个小补充最后分享一个我自己的土办法在排查磁盘问题时我会给du加一个时间戳对比。比如今天记录一次du -sh /var明天再记录一次看差异。单纯看绝对值很难判断是正常增长还是异常膨胀但看增量就一下子暴露问题。做法很简单# 记录基线 du -sh /var /tmp/disk_baseline.txt # 隔天再跑 du -sh /var /tmp/disk_baseline.txt # 对比 cat /tmp/disk_baseline.txt数据一对比那种一夜涨几个G的异常马上就现出原形。这个方法和我前面说的脚本化是一脉相承的都是“让数据替你说话”。du这个命令本身不算复杂难的是怎么顺手、怎么调节参数配合其他命令。希望这些实战细节对你有用下次再遇到磁盘告警心里能更有底。
返回列表