生产环境经常出现这种情况:
df -h
显示:
/ 分区 95%
但是:
du -sh /*
统计出来却只有:
60%
空间凭空消失了。
常见场景:
- Oracle alert.log 被删除
- MySQL slow.log 被删除
- Java GC 日志被删除
- Kafka 日志文件被删除
- 应用日志切割异常
此时:
rm 已经执行 空间却没有回来
很多 DBA 会重启服务。
其实不用。
核心原理
Linux 文件删除后:
目录项删除
但如果进程仍然打开该文件:
inode 仍然存在
空间不会释放。
只有:
进程关闭文件描述符 或者: 进程退出
空间才会真正归还给文件系统。
快速排查命令
方法一:lsof(推荐)
lsof | grep deleted
输出示例:
java12345 oracle15w REG253,0120G1234567 /tmp/gc.log (deleted)
含义:
PID=12345 占用120G 文件已删除
只看大文件
lsof -nP | grep deleted
或者:
lsof | grep deleted | awk'{print $7,$1,$2,$9}' |sort -nr
输出:
128849018880java1234568719476736mysqld2345
查看某个进程占用的删除文件
例如:
ls -l /proc/12345/fd
输出:
15 ->/tmp/gc.log (deleted)
释放空间(无需重启)
假设:
PID=12345FD=15
执行:
echo > /proc/12345/fd/15
或者:
cat /dev/null > /proc/12345/fd/15
立即释放空间。
查看释放效果
执行前:
df -h 95%
执行后:
df -h 60%
Oracle 场景
经常发生:
rm alert_orcl.log
但监听或数据库进程仍然持有文件。
排查:
lsof | grep alert
输出:
oracle12345 oraclealert_orcl.log (deleted)
处理:
echo > /proc/12345/fd/7
MySQL 场景
典型情况:
rm mysql-slow.log
mysqld 仍然持有文件。
查看:
lsof | grep mysql-slow
推荐处理:
FLUSH SLOW LOGS;
或者:
FLUSH LOGS;
而不是直接删除日志。
一键巡检脚本
#!/bin/bashecho"Deleted file check..."lsof -nP | grep deleted | \ awk' { size=$7/1024/1024/1024; printf "%-10s %-8s %10.2f GB\n",$1,$2,size; } ' |sort -k3 -nr
输出:
java12345120.33GBmysqld223445.20GBoracle889912.50GB
注意事项
1. 不要直接 kill -9
很多人:
kill -9 PID
虽然空间能释放:
但可能导致:
- Oracle 实例恢复
- MySQL Crash Recovery
风险较高。
2. 优先截断文件
推荐:
echo > /proc/PID/fd/FD
风险最低。
3. Docker 环境更容易发生
容器日志:
json.log
经常达到几十 GB。
建议定期检查:
lsof | grep deleted
4. 日志轮转优先使用 logrotate
不要:
rm logfile
推荐:
logrotate
或者应用自带:
reload logs
生产环境使用建议
建议加入监控:
lsof | grep deleted |wc -l
以及:
lsof | grep deleted | awk'{sum+=$7} END {print sum}'
统计已删除文件占用空间。
当超过:
10GB
触发告警。
很多“磁盘突然满了”的故障,实际上不是数据增长,而是被删除但仍被进程持有的大文件造成的。