Linux磁盘还有空间却提示No space left on device?inode占满排查与清理教程

Linux磁盘还有空间却提示No space left on device?inode占满排查与清理教程

Linux服务器明明还有几十GB可用空间,上传文件、写入日志、创建缓存或安装软件时,却提示:

No space left on device

很多人看到这条错误后,会立即寻找大文件或扩容磁盘。但Linux文件系统不仅需要数据块保存文件内容,还需要inode记录文件的类型、权限、时间戳及数据位置等元数据。如果inode已经耗尽,即使磁盘容量仍有大量剩余空间,系统也无法继续创建新文件。

inode占满通常发生在存在海量小文件的服务器上,例如网站缓存、PHP会话、邮件队列、临时文件、监控数据、构建缓存和容器存储目录。

本文介绍如何区分磁盘容量不足与inode耗尽,定位消耗inode最多的目录,并安全清理异常小文件。命令主要适用于常见Linux发行版;不同文件系统、Coreutils版本和业务环境可能存在差异,删除前必须确认文件用途。

一、inode是什么

在ext4等传统Unix文件系统中,每个文件或目录通常都需要一个inode。inode保存文件的元数据,例如:

  • 文件类型。
  • 所有者和用户组。
  • 访问权限。
  • 时间戳。
  • 文件大小。
  • 数据块位置。
  • 硬链接数量。

文件名通常保存在目录项中,目录项再关联到inode编号。因此,一个大小为0字节的空文件也会占用inode;目录、符号链接和普通文件同样需要inode。

这意味着,一百万个1KB小文件占用的数据空间可能不算大,却会消耗大约一百万个inode,并带来目录遍历、备份和删除效率下降等问题。

二、先同时检查磁盘容量和inode

查看文件系统容量:

df -h

重点关注发生报错目录所在的挂载点,而不是只看根分区。

例如,网站位于/data/www时,可以直接执行:

df -h /data/www

查看inode使用情况:

df -i

也可以只查看目标路径所在文件系统:

df -i /data/www

典型输出类似:

Filesystem      Inodes   IUsed   IFree IUse% Mounted on
/dev/vdb1      6553600 6553600       0  100% /data

其中:

  • Inodes:文件系统可用inode总数。
  • IUsed:已经使用的inode数量。
  • IFree:剩余inode数量。
  • IUse%:inode使用率。

如果df -h显示仍有空间,而df -i中的IUse%达到100%,基本可以确认无法创建文件的直接原因是inode耗尽。

GNU Coreutils的df --inodesdf -i用于显示inode使用情况。某些文件系统可能不提供传统或固定数量的inode统计,输出应结合实际文件系统类型理解。

三、确认报错路径属于哪个文件系统

一台服务器可能同时挂载系统盘、数据盘、临时文件系统和容器卷。根分区有空间,不代表目标目录所在分区也有空间。

查看目标路径对应的文件系统:

df -hT /path/to/problem
df -i /path/to/problem

查看挂载关系:

findmnt -T /path/to/problem

例如:

findmnt -T /var/lib/docker

如果报错发生在Docker容器内部,还需要分别检查:

  • 容器可写层。
  • 绑定挂载目录。
  • Docker数据根目录。
  • 宿主机对应文件系统。
  • 容器或项目设置的存储配额。

不要因为/分区有空间,就忽略独立挂载的/var/home/tmp或数据盘。

四、快速定位哪个一级目录消耗inode最多

GNU du支持通过--inodes统计目录树中的inode数量。先在目标文件系统的挂载点执行:

sudo du --inodes -x -d 1 / 2>/dev/null | sort -n

参数含义:

  • --inodes:统计inode数量,而不是文件大小。
  • -x:不跨越到其他文件系统。
  • -d 1:只显示一层目录。
  • sort -n:按数值排序。

如果问题发生在/var

sudo du --inodes -x -d 1 /var 2>/dev/null | sort -n

发现某个目录数量异常后继续向下:

sudo du --inodes -x -d 1 /var/lib 2>/dev/null | sort -n

然后重复缩小范围,直到找到具体应用目录。

部分较旧环境的du可能不支持--inodes-d。可以先查看:

du --help | grep -E 'inodes|max-depth'

也可使用--max-depth=1替代-d 1

sudo du --inodes -x --max-depth=1 /var 2>/dev/null | sort -n

五、使用find统计文件数量

如果系统中的du不支持inode统计,可以使用find逐层计数。

统计目录树内的普通文件数量:

sudo find /target/path -xdev -type f -printf '.' 2>/dev/null | wc -c

统计所有目录项:

sudo find /target/path -xdev -mindepth 1 -printf '.' 2>/dev/null | wc -c

GNU find支持-printf,但BusyBox或部分非GNU环境可能不支持。兼容性更高但速度可能较慢的写法是:

sudo find /target/path -xdev -type f -print 2>/dev/null | wc -l

逐个比较一级子目录:

for dir in /var/*; do
  [ -d "$dir" ] || continue
  count=$(find "$dir" -xdev -mindepth 1 -printf '.' 2>/dev/null | wc -c)
  printf '%12d %s\n' "$count" "$dir"
done | sort -n

在包含数百万文件的目录中,finddu可能运行较久并产生明显I/O。生产环境应从最可疑的挂载点开始,避免无目的扫描整台服务器。

六、常见inode占满目录

1. 网站缓存目录

WordPress、Laravel、ThinkPHP及其他应用可能生成大量页面缓存、模板缓存、图片缩略图或临时文件。

常见位置包括:

/var/www/*/cache
/var/www/*/storage/framework
/var/www/*/wp-content/cache
/tmp

不要直接套用路径删除。不同插件和框架的缓存结构不同,应优先使用应用自身的清理命令或管理后台。

2. PHP会话文件

使用文件保存PHP会话时,大量过期会话可能堆积在:

/var/lib/php/sessions
/var/lib/php/session

查看实际配置:

php -i | grep -E '^session.save_path|^session.gc_'

如果会话垃圾回收未正常执行、过期时间过长或网站遭受大量自动请求,会话文件可能快速增长。

3. 邮件队列

Postfix、Exim等邮件服务在投递失败、账号被滥用或网站持续发送垃圾邮件时,队列中可能积累大量小文件。

Postfix查看队列:

postqueue -p

不要直接进入邮件队列目录使用rm删除。应先查明积压原因,再使用邮件系统提供的队列管理工具处理,否则可能破坏队列状态。

4. 日志与轮转异常

正常日志通常消耗的是磁盘容量,但某些应用会按请求、任务或容器创建独立日志文件,从而快速消耗inode。

检查日志目录:

sudo du --inodes -x -d 2 /var/log 2>/dev/null | sort -n | tail

如果日志轮转配置错误,可能留下大量压缩文件、临时文件或按日期分割的小日志。

5. Docker和容器数据

Docker镜像层、容器可写层、构建缓存和应用生成的小文件,都可能集中在Docker数据目录。

查看Docker根目录:

docker info --format '{{.DockerRootDir}}'

查看Docker资源占用:

docker system df
docker system df -v

不要直接删除/var/lib/docker/overlay2中的文件。绕过Docker管理机制手动删除可能导致镜像、容器和文件系统元数据不一致。

6. 监控、任务和构建缓存

还应检查:

  • CI/CD工作目录和构建缓存。
  • 软件包管理器缓存。
  • 爬虫或采集程序的临时文件。
  • 队列任务和失败任务目录。
  • 监控系统产生的分片文件。
  • 备份程序的临时目录。
  • 用户上传后未合并的分片文件。

七、安全清理前先做哪些确认

找到文件很多的目录后,不要立即执行rm -rf。先抽样检查文件类型、时间和所属进程。

查看部分文件:

sudo find /target/path -xdev -type f | head -n 30

查看最近修改的文件:

sudo find /target/path -xdev -type f -printf '%TY-%Tm-%Td %TH:%TM %p\n' 2>/dev/null | sort | tail -n 30

查看较早的文件:

sudo find /target/path -xdev -type f -mtime +30 -print | head -n 30

确认目录是否被进程使用:

sudo lsof +D /target/path

lsof +D会递归扫描目录,在文件极多时可能很慢。也可以先查看应用配置、服务状态和少量文件,再决定是否需要执行。

清理前至少确认:

  1. 文件确实可重新生成或已经过期。
  2. 业务程序不会依赖这些文件恢复状态。
  3. 文件不属于数据库、邮件队列或容器内部元数据。
  4. 删除条件不会匹配到新文件或错误目录。
  5. 必要数据已有可验证的备份。
  6. 清理期间不会与写入任务发生冲突。

八、如何分批删除海量小文件

在数百万文件的目录中,直接执行:

rm /target/path/*

可能因为Shell展开出过多参数而出现Argument list too long,还可能误删子目录或隐藏文件。

确认目标无误后,可以使用find -delete

sudo find /target/path -xdev -type f -mtime +30 -delete

但执行前必须先去掉-delete预览:

sudo find /target/path -xdev -type f -mtime +30 -print | head -n 100

只有预览结果完全符合预期,才能加入-delete

如果希望降低单次删除压力,可以分批处理:

sudo find /target/path -xdev -type f -mtime +30 -print0 |
  xargs -0 -r -n 1000 rm -f

注意:

  • 删除大量文件本身会产生磁盘I/O。
  • 删除正在使用的会话、缓存或任务文件可能影响在线业务。
  • mtime +30不代表文件一定安全,应根据应用生命周期确定。
  • 目录名和变量为空时可能造成严重误操作,生产环境应使用明确绝对路径。

某些应用支持直接删除并重建整个缓存目录,这可能比逐个删除数百万文件更快。但只有在确认目录是独立缓存、权限和SELinux上下文可以正确恢复时才能使用。

九、删除文件后inode为什么没有立即释放

如果文件已经被删除,但仍被进程打开,其目录项会消失,数据块通常要等到进程关闭文件描述符后才会释放。

这种情况更常导致dfdu显示不一致,inode通常也会在最后一个链接删除且不再被进程引用后才释放。

检查已删除但仍打开的文件:

sudo lsof +L1

如果发现日志文件被服务长期打开,应优先让程序重新打开日志,例如使用服务自身的日志重载方式。不要盲目结束数据库或关键服务进程。

清理后重新检查:

df -h /target/path
df -i /target/path

十、inode用完后怎样快速恢复服务

当inode已经100%且业务无法创建文件时,处理顺序应当是:

  1. 确认具体满载的文件系统。
  2. 找到一个明确可删除的缓存或临时目录。
  3. 先删除少量安全文件,释放一部分inode。
  4. 恢复日志、会话或关键服务的基本写入能力。
  5. 再继续分批清理主要文件。
  6. 修复产生海量文件的程序或清理机制。
  7. 建立inode监控和自动清理策略。

应急阶段不需要一口气删除全部文件。先释放几千或几万个inode,通常就能让系统重新创建日志、PID文件和临时文件,为后续操作留出空间。

十一、如何从根本上解决inode不足

1. 修复文件无限增长的原因

常见措施包括:

  • 启用并验证应用缓存清理任务。
  • 修复PHP会话垃圾回收。
  • 为临时文件设置明确生命周期。
  • 限制失败任务和重试次数。
  • 修复邮件发送异常或账号滥用。
  • 配置日志轮转和保留周期。
  • 定期清理CI构建与软件包缓存。
  • 避免为每个请求创建独立文件。

2. 改变数据存储方式

如果业务本身需要保存海量小对象,可以考虑:

  • 将会话迁移到Redis等合适的集中式存储。
  • 将对象文件迁移到对象存储。
  • 将大量小记录保存到数据库或专用存储系统。
  • 将小文件打包归档,减少单文件数量。
  • 按日期或哈希拆分目录,避免单目录过大。

选择方案时应考虑读取方式、并发、备份、生命周期和恢复需求,而不是只为了降低inode数量盲目迁移。

3. 重新规划文件系统

ext4文件系统的inode布局通常在创建文件系统时确定。若业务长期需要海量小文件,仅靠清理无法满足需求,可能需要新建具有合适inode规划的文件系统并迁移数据。

查看ext文件系统信息:

sudo tune2fs -l /dev/DEVICE | grep -E 'Inode count|Free inodes|Inode size|Block size'

/dev/DEVICE替换为实际设备。操作块设备前应通过findmntlsblk -f等命令确认设备与挂载点关系。

不要在生产文件系统上直接尝试不熟悉的格式化或底层调整命令。涉及文件系统重建时,应准备完整备份、迁移窗口和回滚方案。

十二、磁盘有空间时还有哪些原因会报ENOSPC

No space left on device并非只能由inode耗尽造成。如果df -hdf -i都正常,还应检查以下情况。

用户或项目配额已满

quota -s
sudo repquota -a

文件系统整体有空间,但特定用户、组或项目达到配额时,也可能无法继续写入。

tmpfs已满

/tmp/run/dev/shm或容器目录可能位于内存文件系统:

df -hT /tmp /run /dev/shm

ext文件系统保留块

ext系列文件系统可能为特权进程保留一部分块。普通用户无法使用这些空间时,df看起来仍可能存在差异。

查看保留块:

sudo tune2fs -l /dev/DEVICE | grep -E 'Reserved block count|Reserved block percentage'

不要在不了解用途时把保留比例直接改为0。根文件系统保留空间可以帮助系统在接近满盘时维持基本运行。

文件系统变为只读

如果文件系统检测到错误后被重新挂载为只读,常见错误通常是Read-only file system,但排查写入失败时仍应检查:

findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /target/path
sudo journalctl -k --since "1 hour ago"

应用自身的空间限制

数据库表空间、容器存储驱动、Kubernetes临时存储、控制面板或托管平台可能设置独立限制。此时主机上的df并不能反映应用可用额度。

十三、建立inode监控

服务器监控除了磁盘容量,还应记录:

  • 每个文件系统的inode使用率。
  • 剩余inode数量。
  • 重点缓存、会话和邮件目录的文件数量。
  • 小文件产生速度。
  • 清理任务是否成功。
  • Docker或构建缓存增长情况。

可以定期执行:

df -iP

不要只在inode达到100%时告警。文件数量增长很快的服务器,应在80%或更早阶段预警,并结合增长速度设置阈值。

清理脚本也不应无限制运行。建议加入明确目录、文件类型、最小保留时间、单次删除上限和执行日志,先在测试环境验证。

十四、推荐的完整排查顺序

遇到No space left on device时,可以按以下顺序操作:

  1. 使用df -hT /报错路径检查目标文件系统容量和类型。
  2. 使用df -i /报错路径检查inode使用率。
  3. 使用findmnt -T /报错路径确认真实挂载点。
  4. 使用du --inodes -x -d 1逐层寻找文件最多的目录。
  5. 抽样检查文件名称、时间、所有者和应用用途。
  6. 优先使用应用自身的缓存、会话或队列清理工具。
  7. 必须手动删除时,先预览条件,再分批清理。
  8. 使用lsof +L1检查已删除但仍被进程打开的文件。
  9. 再次运行df -hdf -i确认释放结果。
  10. 修复产生海量文件的根本原因并建立inode监控。
  11. 如果inode并未耗尽,继续检查配额、tmpfs、保留块和应用限制。

总结

Linux磁盘还有空间却提示No space left on device时,inode耗尽是最常见原因之一。容量和inode是两套不同的资源:df -h用于检查数据块空间,df -i用于检查inode使用情况。

排查时应先确认报错路径所在的真实文件系统,再使用du --inodesfind逐层定位海量小文件。常见来源包括网站缓存、PHP会话、邮件队列、临时文件、监控数据和Docker应用数据。

清理前必须确认文件用途,优先使用应用自身管理工具,不要直接破坏邮件队列、数据库或Docker内部目录。最终还应修复文件不断产生的原因,并监控inode使用率,否则即使临时释放空间,问题仍会再次出现。

知识库

MySQL占用内存过高怎么办?原因排查、参数优化与安全处理方法

2026-8-7 11:47:35

知识库

腾讯云数据库使用与管理指南:提升数据处理效率的最佳实践

2024-10-26 17:50:53