⭐ 推荐:社区规则条款 V1.0

服务器磁盘 inode 满了导致网站无法上传文件怎么排查?

chinaz
chinaz 归队站长新人站长
发布于 2026-10-14 10:58 ·1 浏览 ·2 回复
内容摘要

服务器上传文件报磁盘空间不足而 df -h 仍有剩余时,用 df -i 确认 inode 耗尽,再通过 du --inodes 逐层定位产生大量小文件的目录,常见为 PHP session、框架缓存、邮件队列等,清理后需防复发。

网站突然上传不了文件,报“磁盘空间不足”但你 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,从源头掐掉邮件堆积。

小结

  1. df -h 正常但 df -i 100%,就是 inode 满,不是容量满。
  2. 用 du --inodes -d 1 逐层钻取,定位到具体目录。
  3. 高发位置:PHP session、框架缓存、postfix maildrop、/tmp。
  4. 删除用 find ... -delete,别用 rm -rf *。
  5. 删完仍不降,用 lsof -nP +L1 查占用进程并重启。
  6. 长期靠 GC 配置 + 定时清理 crontab + XFS 文件系统防复发。
服务器磁盘 inode 满了导致网站无法上传文件怎么排查?,本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-1950.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 2

周末
周末 新人站长 1楼 2026-10-14 11:21

**这个排查路径是对的,但清理环节别用 rm -rf,用 rsync 空目录同步会快几十倍,而且删完 df -i 不掉基本是因为进程还握着已删除文件句柄。**

补充几个实战里最省时间的点。定位阶段,如果 du --inodes 因为老内核不支持或者目录太大跑不动,可以先看 lsof +L1 2>/dev/null | wc -l,很多时候"删过的"文件并没真正释放 inode,尤其是 PHP-FPM、nginx 这类长驻进程。清理阶段用这个:

mkdir -p /tmp/empty && rsync -a --delete /tmp/empty/ /var/lib/php/sessions/

几秒钟清空几十万小文件,比 find -delete 快一个数量级,也不会把目录本身的属主和权限搞乱。注意只清目录里的内容,别把 sessions 目录本身删掉,否则 selinux 上下文和权限要重建。

liulian
liulian 归队站长新人站长 #1489 2楼 2026-10-14 11:37
周末:**这个排查路径是对的,但清理环节别用 `rm -rf`,用 rsync 空目录同步会快几十倍,而且删完 `df -i` 不掉基本是因为进程还握着已删除文件句柄…

rsync 空目录这招确实是清小文件的正解,我一般会带上 --delete-before,避免它先把整个文件列表堆进内存再动手,几十万个文件时省时省内存都明显。lsof +L1 那步也很关键——不少兄弟清完目录 df -i 纹丝不动就是卡在这,确认到句柄后记得 reload 对应服务:

systemctl reload php-fpm nginx

进程不重启,那些已删除的 inode 就一直占着不还。

再补两点防复发。一是 session 堆积本质是 GC 没跑,除了定期清,更该把 session.gc_maxlifetime 调小(常用 1440),并让 session.save_path 指向单独挂载的小分区或 tmpfs,这样即使爆了也只影响 session 盘,不会拖垮根分区。二是已经建好的 ext4 没法在线加 inode,tune2fs -i 只是调预留比例,真想根治只能重新 mkfs 时用 -N 指定数量,或者干脆给这类高频写小文件的目录单独挂块盘。

最后建议挂个监控,df -i 超 85% 就告警,比事后救火省心得多。