问题背景
磁盘空间告警是 Linux 服务器最常见的报警之一。很多人的第一反应是登录服务器直接rm -rf找大文件删掉。但实际场景中,"磁盘满"往往有三种不同情况:
磁盘空间真的满了(df -h 显示 100%)
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 -h Filesystem 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 -i Filesystem 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)
$fordirin/*/;doecho-n"$dir: "; find"$dir"-xdev -typef 2>/dev/null | wc -l;done
# -xdev 限制在同一文件系统,避免统计到挂载点内
# 更精确的方式——逐级排查
$ find / -xdev -typef | awk -F/'{$NF=""; print $0}'| sort | uniq -c | sort -rn | head -10
命令 3:du -sh —— 逐层定位大目录
# 查看根目录下各一级目录的大小 $ du -h --max-depth=1 / | sort -rh | head -10 6.2G /usr 4.1G /var 2.8G /opt 1.5G /home 1.2G /root ... # 发现 /var 大后,再深入 $ du -h --max-depth=1 /var/ | sort -rh | head -10 3.5G /var/log 500M /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 -typef -execdu -ch {} + | tail -1
命令 4:lsof | grep deleted —— 查找已删除但未释放的文件
这是最容易被忽略但实际非常常见的场景。当一个文件被rm删除后,如果有进程仍然持有这个文件的句柄,磁盘空间不会立即释放。
# 检查所有已删除但仍有进程引用的文件 $ sudo lsof | grep deleted COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 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 和 1GB
NAME列末尾的(deleted)标记说明文件已被删除但句柄未释放
FD(File Descriptor)列:23w表示第 23 号文件描述符,打开方式为写入
解决方法(按推荐顺序):
安全方式——通知进程重载文件句柄(大多数日志框架支持):
$kill-USR1# Java / log4j / syslog-ng $ nginx -s reopen # Nginx $ systemctl restart rsyslog
强制方式——如果进程不响应 USR1 信号或不是日志类文件:
# 确认进程是否可以重启 $ systemctl restart# 或直接 kill 进程(确认不影响业务后) $kill-9
临时释放——清空文件内容而非删除文件,不依赖信号:
$ : > /proc//fd/
这不会关闭进程的文件描述符,而是将文件内容截断为 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 -typef -size +100M -execls -lh {} ; 2>/dev/null | sort -k5 -rh | head -20
# 查找特定目录下大于 1GB 的文件
$ find /var/log-typef -size +1G -execls -lh {} ; 2>/dev/null
# 查找最近 7 天内没有修改过的大文件(适合清理历史归档)
$ find /data/archive -typef -size +500M -mtime +7 -execls -lh {} ; 2>/dev/null
参数解释:
-xdev:限制在同一文件系统内,不搜索挂载的其他分区
-size +100M:大于 100MB 的文件(单位:k/M/G)
-exec ls -lh {} ;:对每个结果执行 ls
sort -k5 -rh:按第 5 列(文件大小)反向排序
2>/dev/null:过滤权限不足的错误信息
命令 6:ncdu —— 交互式磁盘分析
ncdu(NCurses Disk Usage)是du的交互式替代品,对逐层排查大目录效率更高:
# 安装 $ apt install ncdu # Debian/Ubuntu $ yum install ncdu # CentOS/RHEL # 使用 $ ncdu /
操作方式:
方向键上下移动
Enter 进入目录
d删除选中的文件/目录
q退出
ncdu 扫描速度比du快(尤其在大目录下),而且可以实时看到各目录大小排序,不需要反复敲命令。
命令 7:iostat -xz —— 分析磁盘 IO 性能
当磁盘使用率没有 100% 但业务响应仍然很慢时,可能是 IO 性能达到上限而非空间不足:
$ iostat -xz 1 5 Linux 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 %util vda 3200.0 4500.0 128000.0 360000.0 0.0 0.0 0.00 0.00 15.20 92.30 256.0 99.8
关键指标:
| 指标 | 含义 | HDD 警戒线 | SSD 警戒线 |
|---|---|---|---|
| %util | 磁盘忙碌百分比 | > 80% | 对 SSD 参考价值有限 |
| r_await / w_await | 读写平均延迟 | > 20ms | > 2ms |
| aqu-sz | 平均队列长度 | > 1 | > N(并行度) |
| r/s + w/s | IOPS | 机械盘 ~200 | 取决于 SSD 型号 |
对 NVMe SSD 的说明:%util对多队列设备(NVMe)不准确,可能显示 100% 但仍能处理更多 IO。更可靠的指标是aqu-sz(平均队列长度)和r_await / w_await(IO 延迟)。
命令 8:journalctl --disk-usage —— 检查 systemd 日志占用
systemd-journald 的日志在不加限制的情况下可能占用数 GB 磁盘空间:
# 查看 journal 日志占用的磁盘空间 $ journalctl --disk-usage Archived and active journals use 3.8G. # 清理 7 天前的日志 $ journalctl --vacuum-time=7d # 限制 journal 最大体积为 500MB $ journalctl --vacuum-size=500M # 永久限制(编辑配置文件) $ cat /etc/systemd/journald.conf [Journal] SystemMaxUse=500M MaxFileSec=7day
修改配置后重启 systemd-journald:
$ systemctl restart systemd-journald
三、按场景组合使用 8 个命令
场景 A:空间占比高,du 能正常定位
执行顺序:df -h→du逐级定位 →find查找大文件 →ncdu交互式确认
$ df -h # 定位满的分区
$ du -h --max-depth=1 / # 逐级深入
$ find /path -typef -size +500M -execls -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 -typef | wc -l # 统计文件总数 $ find /data -xdev -typef -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 -typef -mtime +30 -delete
五、预防措施
日志轮转必须配置——没有 logrotate 的服务器迟早会磁盘满。检查是否存在未被 logrotate 覆盖的日志文件。
监控与告警:不要等到 100% 再告警,分三级告警:
Warning: 使用率 > 80%
Critical: 使用率 > 90%
Emergency: 使用率 > 95%
独立分区:将 /var/log、/data 等容易写满的目录独立分区,避免写满根分区导致系统无法正常启动。
定期巡检:每周自动扫描各分区使用率和大文件增长趋势。
六、注意事项
不要在根分区满时重启服务器——根分区没有剩余空间可能导致重启后部分服务无法正常启动。
%util 100% 不等于磁盘损坏——可能是正常的高负载,先确认 aqu-sz 和 await 是否异常。
df 和 du 的统计差异:正常情况下差异在 5% 以内。如果差异显著,优先检查lsof | grep deleted。
挂载点覆盖:如果在非空目录上挂载新分区,原目录下的文件会被隐藏但仍占用空间。排查时注意mount输出。
**生产环境慎用rm -rf**——可能误删正在写入的日志文件,导致句柄未释放反而无法释放空间。建议优先用echo "" > file或truncate -s 0 file。
七、总结
磁盘满排查的核心路径:
先看 df -h确认空间耗尽的分区和挂载点
再看 df -i排除 inode 耗尽的情况
df 满但 du 找不到→ 执行lsof | grep deleted
逐层 du定位最大目录
find 和 ncdu查找具体大文件
检查 journalctl系统日志占用
配合 iostat排除 IO 性能问题
磁盘满的最佳应对策略不是"发现满了再删",而是通过 logrotate、分区规划、监控预警建立一个防止磁盘写满的体系。磁盘满了之后的每一次删除,都应该有记录、有备份、有确认、有总结。
-
Linux
+关注
关注
88文章
11829浏览量
219630 -
服务器
+关注
关注
14文章
10386浏览量
91785 -
磁盘
+关注
关注
1文章
402浏览量
26598
原文标题:Linux 磁盘满了别急删文件:这 8 个排查命令先跑一遍
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
在Linux下增加磁盘空间的步骤
Linux中的可用磁盘空间如何检查?
简单又快速看懂linux的磁盘划分
详解Linux服务器的用户活动和命令
Steam 客户端 Beta 版更新:针对分配磁盘空间性能优化
linux磁盘空间满了怎么清理
Linux磁盘管理指令合集:从查看、分区到修复
Linux服务器磁盘空间告警的最佳应对策略
评论