磁盘还有空间却写不进去:inode 耗尽的判断与清理顺序
先确认到底缺的是块还是 inode
「磁盘满了」有两种完全不同的情况:数据块用完,或者 inode 用完。前者 df -h 能看出来,后者只有 df -i 才显示:
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/vda1 40G 12G 26G 32% /
df -i
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/vda1 2621440 2621440 0 100% /
上面这个例子块只用了 32%,inode 却 100%。这时再删大文件没用,必须减少文件个数。
每个文件、目录、软链接都占一个 inode。ext4 在格式化时就固定了 inode 总数,事后无法扩容(只能重建文件系统),所以小文件多的场景很容易先耗尽 inode。
找出是谁在制造海量小文件
按目录统计文件数,从根往下逐层收敛:
# 统计各一级目录的文件数(排除挂载点,避免跨盘)
for d in /*; do [ -d "$d" ] && echo "$(find "$d" -xdev -type f 2>/dev/null | wc -l) $d"; done | sort -rn | head
定位到具体目录后,再看文件类型和时间分布:
find /var -xdev -type f -printf '%TY-%Tm-%Td\n' 2>/dev/null | sort | uniq -c | tail -20
最常见的三类元凶:
- 会话文件:
/var/lib/php/sessions、/tmp下的sess_*,过期不清理。 - 邮件队列:
/var/spool/postfix堆积成千上万个小文件。 - 应用缓存:按 key 落盘一文件一对象的缓存目录,或日志按请求切片。
清理的正确顺序
先停掉持续写入的进程,再删,否则一边删一边生成,永远追不上:
systemctl stop php-fpm # 举例,按实际服务替换
find /var/lib/php/sessions -xdev -type f -mtime +1 -delete
find /tmp -xdev -type f -atime +7 -delete
注意两点:-xdev 防止 find 跨到别的挂载点;-mtime 按修改时间、-atime 按访问时间,语义不同,别混用。
大量小文件时 rm 会很慢,可以先用 rsync -a --delete empty/ target/ 快速清空目录,再删空目录本身。
为什么删了文件 inode 还是没释放
如果有进程还持有已删除文件的句柄,inode 不会立刻回收。检查:
lsof +L1 | head -20
输出里 NLINK 为 0 的就是「已删除但仍被打开」的文件。重启对应进程即可释放;直接 kill -9 前先确认业务影响。
怎么避免再次发生
与其等出事,不如提前兜住:
# 监控 inode 使用率,超过 80% 告警
df -i --output=source,pcent,target | awk 'NR>1 {gsub("%","",$2); if ($2>80) print}'
再配合定期清理策略:日志用 logrotate 的 maxage,会话目录挂 tmpfs 或加定时任务,缓存目录设上限。容器环境还要注意镜像层里的小文件,docker system df 看不到 inode,得进到 /var/lib/docker 所在分区单独看。
一句话总结:df -h 正常但报 No space left,第一反应应该是 df -i,然后按目录统计文件数收敛,别急着删大文件。
- 点赞
- 收藏
- 关注作者
评论(0)