Linux 磁盘满了别急删文件:这 8 个排查命令先跑一遍
问题背景
磁盘空间告警是 Linux 服务器最常见的报警之一。很多人的第一反应是登录服务器直接 rm -rf 找大文件删掉。但实际场景中,"磁盘满"往往有三种不同情况:
- inode 耗尽了(df -h 显示还有空间,但系统报 No space left on device)
- 文件已删除但空间未释放(df 显示满,但 du 统计不到大文件)
不分清楚这三种情况就盲目删文件,可能删了半小时空间也没释放,或者删了不该删的文件导致业务异常。
本文按排查顺序逐一讲解 8 个关键命令,覆盖空间排查、inode 排查、已删文件句柄排查、IO 性能排查,最后给出生产环境清理的标准流程。
一、排查前的准备工作
在开始删除任何文件前,先做两件事:
# 记录当前磁盘状态,以便事后回溯$ df -h > /tmp/disk_before_$(date +%s).txt$ df -i >> /tmp/disk_before_$(date +%s).txt# 确认挂载点对应的业务$ mount | grep <挂载点>$ ls -la <挂载点> | head
如果有多块数据盘(如 /data、/var/log 独立分区),先确认哪个分区满了,再针对性排查。
二、8 个命令逐一详解
命令 1:df -h —— 查看各挂载点使用率
$ df -hFilesystem Size Used Avail Use% Mounted on/dev/vda1 40G 38G 16M 100% //dev/vdb1 200G 120G 80G 60% /data
关键检查点:
- **Use% = 100%**:该分区空间已满,需要立即处理
- **Use% = 90~99%**:预警范围,需要排查哪些目录在持续增长
- 关注 Avail 列而非 Used 列:Avail 是实际可用空间(非 root 用户可用),Used 可能包含预留块(默认 5%)
预留块含义:mkfs.ext4 默认保留 5% 给 root 用户,所以非 root 用户看到 Avail 会比预期少。数据盘可以用 tune2fs -m 1 /dev/vdb1 调低到 1%。
命令 2:df -i —— 检查 inode 是否耗尽
$ df -iFilesystem Inodes IUsed IFree IUse% Mounted on/dev/vda1 256000 255900 100 100% /
典型陷阱:df -h 显示还有空间,但系统报 No space left on device,此时 df -i 很可能显示 IUsed = 100%。
inode 耗尽的原因是小文件太多——每个文件(不管大小 1 字节还是 1GB)都需要一个 inode。一个 40GB 的分区如果满了 255 万个小文件,inode 就会耗尽,实际空间可能只用了 10%。
快速定位小文件目录:
# 统计各目录的文件数量(递归深度 1)$ for dir in /*/; doecho -n "$dir: "; find "$dir" -xdev -type f 2>/dev/null | wc -l; done# -xdev 限制在同一文件系统,避免统计到挂载点内# 更精确的方式——逐级排查$ find / -xdev -type f | awk -F/ '{$NF=""; print $0}' | sort | uniq -c | sort -rn | head -10
命令 3:du -sh —— 逐层定位大目录
# 查看根目录下各一级目录的大小$ du -h --max-depth=1 / | sort -rh | head -106.2G /usr4.1G /var2.8G /opt1.5G /home1.2G /root...# 发现 /var 大后,再深入$ du -h --max-depth=1 /var/ | sort -rh | head -103.5G /var/log500M /var/lib...# 直到定位到具体目录$ du -sh /var/log/nginx/1.2G /var/log/nginx/
du 递归统计所有子目录,大目录下逐层深入很快就能找到"罪魁祸首"。
常用变体:
# 只看当前目录下各子目录大小,不递归更深$ du -sh */ .[!.]*/# 用 time 限制范围(只看修改时间早于 N 天的文件)# 统计 30 天前的日志总量$ find /var/log -name "*.log" -mtime +30 -type f -exec du -ch {} + | tail -1
命令 4:lsof | grep deleted —— 查找已删除但未释放的文件
这是最容易被忽略但实际非常常见的场景。当一个文件被 rm 删除后,如果有进程仍然持有这个文件的句柄,磁盘空间不会立即释放。
# 检查所有已删除但仍有进程引用的文件$ sudo lsof | grep deletedCOMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEjava 3456 root 23w REG 202,1 2147483648 12345 /var/log/app/access.log (deleted)nginx 2321 www 5w REG 202,1 1073741824 23456 /var/log/nginx/access.log (deleted)
关键信息解读:
SIZE/OFF 列:文件的实际大小(单位字节),这里是 2GB 和 1GBNAME 列末尾的 (deleted) 标记说明文件已被删除但句柄未释放- FD(File Descriptor)列:
23w 表示第 23 号文件描述符,打开方式为写入
解决方法(按推荐顺序):
安全方式——通知进程重载文件句柄(大多数日志框架支持):
$ kill -USR1 <PID> # Java / log4j / syslog-ng$ nginx -s reopen # Nginx$ systemctl restart rsyslog
强制方式——如果进程不响应 USR1 信号或不是日志类文件:
# 确认进程是否可以重启$ systemctl restart <service># 或直接 kill 进程(确认不影响业务后)$ kill -9 <PID>
临时释放——清空文件内容而非删除文件,不依赖信号:
$ : > /proc/<PID>/fd/<FD_NUMBER>
这不会关闭进程的文件描述符,而是将文件内容截断为 0,立即释放磁盘空间。适用于不能重启进程的生产环境。
为什么会出现这种情况:
最常见的场景是 logrotate。很多默认的 logrotate 配置使用 create 指令,流程是:mv access.log access.log.1 -> create access.log(新建空文件)。新文件有了新 inode,但旧文件的句柄仍然被进程持有,旧文件的磁盘空间一直占用直到进程重启。
正确的做法是 logrotate 配置 copytruncate:
/var/log/nginx/*.log { daily rotate 30 copytruncate # 复制内容后截断原文件,不改变 inode compress delaycompress missingok notifempty}
命令 5:find 查找大文件
# 查找根目录下大于 100MB 的文件$ find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -20# 查找特定目录下大于 1GB 的文件$ find /var/log -type f -size +1G -exec ls -lh {} \; 2>/dev/null# 查找最近 7 天内没有修改过的大文件(适合清理历史归档)$ find /data/archive -type f -size +500M -mtime +7 -exec ls -lh {} \; 2>/dev/null
参数解释:
-xdev:限制在同一文件系统内,不搜索挂载的其他分区-size +100M:大于 100MB 的文件(单位:k/M/G)-exec ls -lh {} \;:对每个结果执行 lssort -k5 -rh:按第 5 列(文件大小)反向排序
命令 6:ncdu —— 交互式磁盘分析
ncdu(NCurses Disk Usage)是 du 的交互式替代品,对逐层排查大目录效率更高:
# 安装$ apt install ncdu # Debian/Ubuntu$ yum install ncdu # CentOS/RHEL# 使用$ ncdu /
操作方式:
ncdu 扫描速度比 du 快(尤其在大目录下),而且可以实时看到各目录大小排序,不需要反复敲命令。
命令 7:iostat -xz —— 分析磁盘 IO 性能
当磁盘使用率没有 100% 但业务响应仍然很慢时,可能是 IO 性能达到上限而非空间不足:
$ iostat -xz 1 5Linux 5.15.0-91-generic ... 08/15/2025 _x86_64_ (32 CPU)Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz %utilvda 3200.0 4500.0 128000.0 360000.0 0.0 0.0 0.00 0.00 15.20 92.30 256.0 99.8
关键指标:
对 NVMe SSD 的说明:%util 对多队列设备(NVMe)不准确,可能显示 100% 但仍能处理更多 IO。更可靠的指标是 aqu-sz(平均队列长度)和 r_await / w_await(IO 延迟)。
命令 8:journalctl --disk-usage —— 检查 systemd 日志占用
systemd-journald 的日志在不加限制的情况下可能占用数 GB 磁盘空间:
# 查看 journal 日志占用的磁盘空间$ journalctl --disk-usageArchived and active journals use 3.8G.# 清理 7 天前的日志$ journalctl --vacuum-time=7d# 限制 journal 最大体积为 500MB$ journalctl --vacuum-size=500M# 永久限制(编辑配置文件)$ cat /etc/systemd/journald.conf[Journal]SystemMaxUse=500MMaxFileSec=7day
修改配置后重启 systemd-journald:
$ systemctl restart systemd-journald
三、按场景组合使用 8 个命令
场景 A:空间占比高,du 能正常定位
执行顺序:df -h → du 逐级定位 → find 查找大文件 → ncdu 交互式确认
$ df -h # 定位满的分区$ du -h --max-depth=1 / # 逐级深入$ find /path -type f -size +500M -exec ls -lh {} \;$ ncdu /path # 交互式确认
场景 B:df 显示满,但 du 统计不到大文件
执行顺序:df -h → sudo lsof | grep deleted → 确认后释放
$ sudo lsof | grep deleted | head -5# 找到最大的(deleted)文件$ sudo lsof | grep deleted | awk '{print $7, $NF, $2}' | sort -rn | head -10# 对 PID 发送信号或重启服务
场景 C:No space left on device 但 df -h 有空间
执行顺序:df -i → find 统计文件数 → 清理小文件
$ df -i # 确认 inode 满$ find /data -xdev -type f | wc -l # 统计文件总数$ find /data -xdev -type f -mtime +90 -delete # 清理 90 天前的旧文件
场景 D:磁盘性能差但空间充足
执行顺序:iostat -xz → iotop -o → pidstat -d
$ iostat -xz 1 5 # 确认 %util 高、await 高$ iotop -o # 定位 IO 密集进程$ pidstat -d 1 5 # 按进程统计 IO
四、生产环境标准清理流程
不要在发现磁盘满时直接上手删文件,遵循以下流程:
记录现场 → 定位大目录 → 确认业务影响 → 备份 → 清理 → 验证
Step 1:记录现场
$ df -h > /tmp/disk_clean_$(date +%s).before$ du -h --max-depth=1 /var/log | sort -rh > /tmp/disk_clean_$(date +%s).logdir
Step 2:定位并确认
# 找到大文件后确认其业务归属$ ls -la /var/log/nginx/access.log.20250815.gz# 与业务方确认是否可以清理
Step 3:备份后再清理
对于不确定的日志/数据文件,压缩备份到其他分区或存储后再清理:
# 备份到其他分区$ gzip -c /var/log/nginx/access.log.20250815.gz > /backup/nginx/access.log.20250815.gz# 确认备份成功后删除$ rm /var/log/nginx/access.log.20250815.gz
Step 4:验证空间是否释放
$ df -h <挂载点>$ df -i <挂载点> # 如果之前 inode 也高的话
Step 5:配置长期策略
根据排查结果配置日志轮转或周期性清理:
# logrotate 示例:按大小轮转/var/log/nginx/*.log { size 500M rotate 14 copytruncate compress missingok notifempty}# crontab 定期清理任务0 3 * * 0 find /data/tmp -type f -mtime +30 -delete
五、预防措施
- 日志轮转必须配置——没有 logrotate 的服务器迟早会磁盘满。检查是否存在未被 logrotate 覆盖的日志文件。
- 监控与告警:不要等到 100% 再告警,分三级告警:
- 独立分区:将 /var/log、/data 等容易写满的目录独立分区,避免写满根分区导致系统无法正常启动。
- 定期巡检:每周自动扫描各分区使用率和大文件增长趋势。
六、注意事项
- 不要在根分区满时重启服务器——根分区没有剩余空间可能导致重启后部分服务无法正常启动。
- %util 100% 不等于磁盘损坏——可能是正常的高负载,先确认 aqu-sz 和 await 是否异常。
- df 和 du 的统计差异:正常情况下差异在 5% 以内。如果差异显著,优先检查
lsof | grep deleted。 - 挂载点覆盖:如果在非空目录上挂载新分区,原目录下的文件会被隐藏但仍占用空间。排查时注意
mount 输出。 - **生产环境慎用
rm -rf**——可能误删正在写入的日志文件,导致句柄未释放反而无法释放空间。建议优先用 echo "" > file 或 truncate -s 0 file。
七、总结
磁盘满排查的核心路径:
- df 满但 du 找不到 → 执行
lsof | grep deleted
磁盘满的最佳应对策略不是"发现满了再删",而是通过 logrotate、分区规划、监控预警建立一个防止磁盘写满的体系。磁盘满了之后的每一次删除,都应该有记录、有备份、有确认、有总结。