rm -rf /var/account/* 刪除 3.3 GB 檔案,但 df -h 顯示 /var 分區使用率仍 98%,而 du -sh /var/* 卻顯示目錄僅佔用 120 KB。經典現象:**檔案已從目錄項移除,但仍有進程持有開啟的檔案描述符(fd),資料塊未真正釋放**。

# df -h /var
Filesystem Size Used Avail Use% Mounted on
/dev/xvda5 3.9G 3.6G 77M 98% /var
# du -sh /var/*
120K account
111M cache
53M log
... (總計遠小於 3.6G)
Linux VFS 允許進程持有已刪除檔案的 fd 繼續讀寫。只有當**最後一個持有該檔案 fd 的進程關閉 fd(或進程結束)**,資料塊才會真正回收至可用空間。
lsof 找出已刪除但仍開啟的檔案# 列出所有已刪除但仍被開啟的檔案
lsof | grep deleted
# 針對特定分區
lsof +L1 /var
# 輸出範例
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
java 12345 root 10u REG 202,5 3.3G 0 123 /var/account/big.log (deleted)
NLINK = 0 表示檔案已無目錄連結(已刪除)。SIZE/OFF 顯示檔案大小(即佔用的未釋放空間)。| 方式 | 指令 | 適用場景 | 風險 |
|---|---|---|---|
| 優雅重啟服務 | systemctl reload rsyslogkill -HUP 12345 | 服務支援平滑重載(如 Nginx、rsyslog、Java 應用配置 JMX) | 低 |
| 強制關閉 fd(進程存活) | ls -l /proc/12345/fd/ | grep deleted: > /proc/12345/fd/10 (truncate fd) | 無法重啟服務,需立即釋放空間 | 中(可能導致寫入錯誤、日誌中斷) |
| 重啟進程/主機 | systemctl restart java-appreboot | 以上均不可行 | 高(服務中斷) |
# 釋放後
df -h /var
Filesystem Size Used Avail Use% Mounted on
/dev/xvda5 3.9G 300M 3.4G 8% /var # 空間恢復正常
logrotate 搭配 sharedscripts、postrotate 呼叫 kill -HUP,避免長期持有舊日誌 fd。log4j2 / logback 的 RollingFileAppender。lsof | grep deleted 定期巡檢,或使用 datadog/prometheus 監控 node_filefd_allocated 異常增長。下次 df 與 du 嚴重不符時,先跑 lsof | grep deleted,鎖定進程、優雅釋放,空間立回。