问题现象#
在一次日常巡检中,发现服务器磁盘空间异常:
1
2
3
4
5
6
7
8
| # df 显示 /data 已用 447G
df -h /data
Filesystem Size Used Avail Use% Mounted on
/dev/sdb1 500G 447G 53G 90% /data
# 但 du 统计只有 52G
du -sh /data
52G /data
|
447G vs 52G,差距近 400G!这不是眼睛花了,也不是 df/du 抽风。

问题定位#
第一步:确认文件系统类型#
1
2
| mount | grep /data
/dev/sdb1 on /data type ext4 (rw,relatime)
|
/data 是 ext4 文件系统(非 btrfs/zfs,排除快照占空间的可能)。
第二步:排查残留文件#
1
2
3
4
5
6
7
| # 检查 lost+found(fsck 残留)
ls -la /data/lost+found/
# 很小,排除
# 检查隐藏的大文件
find /data -type f -size +1G 2>/dev/null | head -20
# 没有异常
|
第三步:关键命令 - 查找已删除但仍被占用的文件#
输出结果:

1
2
3
4
| java 2487 vale 52w REG 253,16 104859430 /data/.../rocketmqlogs/stats.log (deleted)
java 2487 vale 49w REG 253,16 104859836 /data/.../transaction.log (deleted)
java 2487 vale 45w REG 253,16 39150059417 /data/.../store.log (deleted)
java 2487 vale 43w REG 253,16 235962624 /data/.../broker.log (deleted)
|
实锤了!
- 进程:java(RocketMQ Broker,PID 2487)
- 状态:
(deleted) —— 文件已被删除,但进程还在持有 FD - 大小:store.log 单文件 39GB,其他几个加起来几十 GB
为什么 du 看不到这些文件?#
| 命令 | 统计范围 | 当前情况 |
|---|
df -h | 文件系统块使用情况 | 447G 已用 —— 被删除但 FD 未释放的文件仍占用块 |
du -sh | 目录树可见文件 | 52G —— 被删除的文件在目录中已不可见 |
这就是那近 400G 差距的来源。
根因分析#
发生了什么?#
- RocketMQ Broker 持续写入日志文件
- 运维/脚本执行了
rm -f store.log 等日志清理 - 但是:Broker 进程没有重启,JVM 仍然持有旧的文件描述符(FD)
- 结果:磁盘空间被"幽灵文件"占用,但文件系统目录里已经看不到
为什么 RocketMQ 容易出现这个问题?#
- RocketMQ 默认使用 logback/log4j 记录日志
- 不支持标准的
logrotate rename + truncate 机制 - 只要外部删除日志而 Broker 不重启,必中这一刀
解决方案#
紧急恢复(推荐)#
重启 RocketMQ Broker,释放被占用的 FD:
1
2
3
4
5
6
7
8
9
10
11
| # 找到 Broker 进程
ps -ef | grep runbroker
# 方式1:手动启动模式
cd /data/timevale/esign/public/rocketmq/bin
./mqshutdown broker
sleep 5
./runbroker.sh &
# 方式2:systemd 模式
systemctl restart rocketmq-broker
|
重启瞬间:
- 所有
(deleted) FD 关闭 - 400G+ 空间立刻回收
df -h 数字会"跳变"
验证:
1
2
| df -h /data
# 现在 Used 应该回到 ~50G 左右
|
不推荐:不重启清空 FD(风险自担)#
如果确实不能重启 Broker(生产高峰期):
1
2
| # 直接清空特定 FD(示例:PID 2487 的 fd 45)
: > /proc/2487/fd/45
|
⚠️ 风险:
- 可能破坏 RocketMQ 日志完整性
- JVM 不一定能正确处理
- 不建议在生产环境使用
预防措施#
方案 A:时间滚动 + 定期重启(最稳)#
1
2
3
| # 日志只按时间保留,不直接 rm
# 定期(如凌晨 4 点)重启 Broker
0 4 * * * systemctl restart rocketmq-broker
|
优点:简单可靠,RocketMQ Broker 重启成本极低
缺点:有短暂服务中断
方案 B:官方日志滚动配置(推荐生产)#
修改 logback_broker.xml / log4j2.xml:
1
2
3
4
5
6
7
| <RollingFile name="RocketmqBroker" fileName="${LOG_HOME}/broker.log">
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="1GB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
|
要点:
- 禁止外部 logrotate
- 所有清理交给 JVM 自己处理
方案 C:监控告警#
1
2
3
4
5
6
7
8
9
10
11
12
| # 创建监控脚本 /usr/local/bin/check_df_du_diff.sh
#!/bin/bash
MOUNT=/data
DF_USED=$(df -m $MOUNT | awk 'NR==2{print $3}')
DU_USED=$(du -sm $MOUNT 2>/dev/null | awk '{print $1}')
DIFF=$((DF_USED - DU_USED))
# 如果差距超过 50GB,告警
if [ $DIFF -gt 51200 ]; then
echo "[ALERT] $MOUNT df/du 差异 ${DIFF}MB,可能存在 FD 泄漏" | logger -t disk_monitor
# 发送告警通知...
fi
|
添加到 crontab:
1
| */10 * * * * /usr/local/bin/check_df_du_diff.sh
|
排雷 Checklist#
| 检查项 | 操作 | 目的 |
|---|
| 日志轮转后 | systemctl reload nginx 或重启应用 | 释放旧 FD |
| 自研服务 | 不要无限写单文件,定期切割 + reload | 防止单文件膨胀 |
| 空间报警 | 同时看 df 和 du,对比差异 | 早期发现 FD 泄漏 |
| Java 服务 | 优先使用 JVM 内置日志滚动 | 避免外部 rm 导致泄漏 |
一句话总结#
你的 400G 磁盘空间没有消失,是被"删了但还在用"的文件幽灵抱着不撒手 👻
看到 df 高、du 低、Java 服务、日志目录 → 不用想,先执行 lsof +L1
参考命令速查#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 查看已删除但仍被占用的文件
lsof +L1
# 按目录筛选
lsof +L1 | grep /data
# 查看特定进程打开的删除文件
lsof -p <PID> | grep deleted
# 查看进程打开的 FD 列表
ls -la /proc/<PID>/fd/
# 统计各进程持有的删除文件大小
lsof +L1 | awk '{s+=$7} END {print "Total bytes:", s}'
|
本文记录于 2026-02-06,CentOS 7 + RocketMQ 环境