网站突然上传不了文件,报“磁盘空间不足”但你 df -h 一看还剩几十 G——这篇就带你一步步确认是不是 inode 用满,并定位到具体是哪个目录在疯狂产生小文件,最后清理干净并防止复发。
第一步:确认到底是空间满还是 inode 满
这一步要拿到两个数字:磁盘块使用率和 inode 使用率。登录服务器(SSH 或宝塔/云厂商 VNC),执行:
df -h
df -i
df -h 看的是容量(Size/Used/Avail/Use%),df -i 看的是 inode 数量(Inodes/IUsed/IFree/IUse%)。
如果 df -h 显示 Use% 只有 60%,而 df -i 里某个分区的 IUse% 是 100%,那就是 inode 耗尽,不是空间不够。
注意:inode 是文件系统为每个文件/目录分配的“身份证”,一个文件占一个 inode。哪怕每个文件只有 1 字节,几十万个小文件也能把 inode 吃光,此时容量还剩很多,但新文件一个也创建不了。
第二步:锁定是哪个分区满了
看 df -i 输出里 IUse% 最高的那一行的 Mounted on 列,比如是 / 还是 /home 还是 /www。
df -i /home
这一步的结果就是“作案分区”,后面所有查找都在这个分区的挂载点内进行。
注意:/proc、/sys、tmpfs 这类挂载点的 inode 数字不用管,它们不占真实磁盘。带 -xdev 参数能让 find 不跨分区,避免把别的盘算进来。
第三步:找出 inode 消耗最多的目录
用 du 的 inode 统计(coreutils 8.22 以上支持):
du --inodes -d 1 / | sort -rn | head -20
如果版本太老,用这个替代:
for d in /*; do echo -n "$d "; find "$d" -xdev -type f 2>/dev/null | wc -l; done | sort -k2 -rn | head
拿到一个大目录后,逐层往下钻,比如 /var 很大就执行 du --inodes -d 1 /var | sort -rn | head -20,一直钻到某个具体目录文件数异常为止(正常目录一般几千个文件,出现几十万就是它了)。
第四步:对照常见元凶逐个排查
下面几个位置是 90% 的情况,按顺序看:
- PHP session 目录:
/var/lib/php/sessions 或 /tmp,文件名是 sess_xxxxxx。GC 没生效时会堆积几十万个。
- 框架缓存/运行时目录:如
/www/wwwroot/站点/runtime/cache、wp-content/cache。
- 邮件队列:
/var/spool/postfix/maildrop,通常是 crontab 报错邮件堆积。
- 上传/临时目录:
/tmp、网站自己的 uploads/ 里被刷了大量小文件。
- 日志切割残留:
/var/log 下大量 .1、.gz 分片。
ls /var/lib/php/sessions | wc -l
du --inodes -d 1 /var/spool/postfix | sort -rn
ls /var/spool/postfix/maildrop | head
第五步:清理并验证
删除时不要用 rm -rf /path/*,参数过大会报 Argument list too long。正确做法:
find /var/lib/php/sessions -type f -name 'sess_*' -mtime +1 -delete
find /path/to/cache -type f -delete
清邮件队列前先停服务,否则文件会边删边生成:
systemctl stop postfix
find /var/spool/postfix/maildrop -type f -delete
systemctl start postfix
清完再跑一次 df -i 确认 IUse% 降下来,然后去网站后台试传一张图片。
注意:如果 df -i 降不下来,说明有进程还占着已删除的文件。执行 lsof -nP +L1 找出 LINK 数异常的进程,重启对应的 php-fpm 或 nginx 即可释放。
第六步:防止再次被写满
- 在
/etc/php.ini 里设 session.gc_probability = 1、session.gc_maxlifetime = 3600,并重启 php-fpm。
- 加一条 crontab:
0 4 * * * find /var/lib/php/sessions -type f -mtime +1 -delete。
- 建站时优先用 XFS 文件系统,它的 inode 是动态分配的,不会像 ext4 那样提前固定数量。
- crontab 命令后面加
>/dev/null 2>&1,从源头掐掉邮件堆积。
小结
df -h 正常但 df -i 100%,就是 inode 满,不是容量满。
- 用
du --inodes -d 1 逐层钻取,定位到具体目录。
- 高发位置:PHP session、框架缓存、postfix maildrop、/tmp。
- 删除用
find ... -delete,别用 rm -rf *。
- 删完仍不降,用
lsof -nP +L1 查占用进程并重启。
- 长期靠 GC 配置 + 定时清理 crontab + XFS 文件系统防复发。