磁盘还有空间却报 No space left on device:一次 inode 耗尽的复盘

一个写大量小文件的消息服务疯狂刷 No space left on device 新文件写不进,df -h 一看根分区只用了 58% 还剩 42G 空着,空间明明有为什么报没空间。排查梳理:lsof grep deleted 排除了已删文件句柄占空间,df -i 一看 inode 用满了 IUse 100%,那个目录里 580 万个几 KB 的小临时文件正好把 inode 总数耗尽;一个文件系统有两种独立的会被用尽的配额数据块存文件内容就是磁盘空间 GB、inode 存文件元信息和索引每个文件占恰好一个,两者总量在格式化时就定死彼此独立,所以有两种满空间满 df -h 100% 和 inode 满 df -i 100% 任意一种都建不了新文件;inode 是文件的身份证加索引卡记权限属主大小时间戳和内容数据块索引文件名不在 inode 里在目录那张名字到 inode 号对照表里,关键是不管文件多大多小哪怕 0 字节空文件都恰好占一个 inode,文件个数决定 inode 用量文件大小决定空间用量两者无关海量小文件就让 inode 先耗尽;No space left on device 底层是同一个错误码 ENOSPC 空间满和 inode 满都报它它不区分,排查纪律是一报 No space 就 df -h 和 df -i 两个都敲哪个 100% 就是哪个的问题;确认 inode 满后找文件个数最多而不是最大的目录用 for 循环 find wc -l 再 sort -rn,常见偷攒几百万文件的地方 PHP session、/var/spool 邮件队列、应用缓存临时目录,目录文件极多时 ls 会卡死要用 ls -f 不排序;清理用 find -delete 别用 rm * 会 Argument list too long,整目录能弃就 mv 走再后台 rm -rf 服务立刻恢复,只删一次会复发必须根治让程序自清理加 cron 定时清理,天生海量小文件的盘要 mkfs 时用 -i 调高 inode 密度。正确做法是报 No space 先 df -h 和 df -i 都敲,以及一套磁盘空间与 inode 排查纪律。

2021 年,一次"df -h 明明白白告诉我磁盘还剩 40% 空间,服务却死活报 No space left on device"的事故,把我对"磁盘满了"这件事的理解,从头到尾翻新了一遍。那台服务器跑着一个消息处理服务,某天下午开始,日志里疯狂刷 java.io.IOException: No space left on device,新文件一个都写不进去。我心里咯噔一下:磁盘满了呗,这还用查?我登上去,df -h 一敲,准备看那个躺着的 100%——结果屏幕上写着,根分区用了 58%,还剩 40 多个 G 空着。我愣住了。我以为是我看错了分区,又仔细核对了挂载点,没错,就是服务在写的那个分区,实实在在空着 40 多个 G。我又怀疑是不是某个进程把空间占了又没释放(占着已删除文件的句柄),查了一圈 lsof,也不是。可服务那边,No space left on device 一个字不少,还在刷。我盯着 df -h 那个 58%,脑子里全是问号:操作系统跟我说"没空间了",我亲眼看见"明明有 40 个 G 的空间"。我们俩,看的【是同一块磁盘】吗?如果是,那它说的"空间",和我看见的"空间",到底【是不是同一个东西】?如果不是——那磁盘上,除了我以为的那个"空间",还藏着【另一种】会被耗尽的东西?这件事逼着我把磁盘的两种配额、inode 是什么、df -i、海量小文件,还有"我以为的满"和"系统说的满"的天壤之别,彻底理清了。本文复盘这次实战。

问题背景

环境:CentOS 7,根分区 ext4,一个写大量小文件的消息服务
事故现象:
- 服务疯狂刷 No space left on device,新文件写不进
- ★ df -h 一看:根分区用了 58%,还剩 40 多 G 空着
- 空间明明有,为什么报"没空间"?

现场排查:
# 1. ★ 看磁盘空间用量 —— 空间确实有
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1       100G   58G   42G  58% /         # ★ 还剩 42G!

# 2. ★ 那查查是不是"已删除但句柄没放"占了空间
$ lsof | grep deleted
# (没有可疑的大文件)                            # ★ 不是这个原因

# 3. ★★ 关键转折:看 inode 用量(df -i)
$ df -i
Filesystem      Inodes   IUsed   IFree IUse% Mounted on
/dev/vda1      6553600 6553600       0  100% /   # ★★ inode 用满了!
#                      ^^^^^^^ ^^^^^^^^^^^ ★ 100%

# 4. ★ 找出是谁吃光了 inode —— 哪个目录小文件最多
$ for d in /var/* /data/* /tmp/*; do \
    echo "$(find $d 2>/dev/null | wc -l) $d"; done | sort -rn | head
# 5800000 /data/msgcache                       # ★★ 580 万个文件!

# 5. ★ 看看那个目录里是什么
$ ls /data/msgcache | head
msg-0001.tmp  msg-0002.tmp  msg-0003.tmp ...    # ★ 海量小临时文件
$ ls /data/msgcache | wc -l
5800000                                         # ★ 580 万个,每个几 KB

根因(后来想清楚的):
1. ★ 一个文件系统,有【两种】独立的"配额":
   一种是"数据块"(放文件内容,就是空间 GB);
   另一种是"inode"(放文件元信息,一个文件占一个)。
2. ★ 这两种配额,在格式化时就【各自定好了总数】,
   彼此【独立】—— 一种用尽,另一种再空也没用。
3. ★ 那个消息服务,产生了 580 万个【几 KB 的小
   文件】。小文件占的空间(数据块)极少 -> 空间
   只用了 58%;但【每个文件都要吃掉一个 inode】。
4. ★ 580 万个文件 = 吃掉 580 万个 inode,正好把
   文件系统的 inode 总数耗尽 -> IUse 100%。
5. ★ inode 没了,就【再也建不了任何新文件】——
   哪怕只建一个 0 字节的空文件也不行,因为建文件
   的第一步就是【分配一个 inode】。内核此时返回的
   错误码,就是 ENOSPC -> "No space left on device"。
6. 真相:报错信息里的 "space",是个有歧义的词。
   它说的不是"没有空间(GB)了",而是"没有
   inode 可分配了"。我一直在用 df -h 找空间,
   而真正耗尽的,是 df -i 才看得见的 inode。
不是磁盘满了,是 inode 满了 —— 它俩是
两种东西,No space 这句话,我理解错了对象。

修复 1:一个文件系统,有"两种"会被用尽的东西

# === ★ 先建立最关键的认知:磁盘有两种"配额" ===

# === ★ 把一块格式化好的磁盘,想象成一个仓库 ===
# 这个仓库,要存"文件"。但存一个文件,需要两样东西:
# ★ 东西 1:数据块(data block)—— 存文件的【内容】。
#   你那个文件里 1MB 的数据,就实实在在占 1MB 的
#   数据块。这就是我们平时说的"磁盘空间 / GB"。
# ★ 东西 2:inode —— 存文件的【元信息 + 索引】。
#   一个文件的名字归属、权限、大小、时间戳,以及
#   "它的内容散落在哪些数据块上"的索引,都记在
#   它的 inode 里。★ 每个文件,占用【恰好一个】inode。

# === ★ 关键:这两样东西的总量,格式化时就定死了 ===
# ★ 你 mkfs 格式化一个文件系统时,它就按一个比例,
#   把磁盘【一次性】划分好:多少空间给数据块,
#   多少空间给 inode 表 —— inode 的【总个数】,
#   这一刻就固定了,之后【不能动态增加】。
$ df -h /        # 看"数据块"这种配额的用量(就是空间)
$ df -i /        # ★ 看"inode"这种配额的用量(文件个数)

# === ★ 于是,有两种完全不同的"满" ===
# ★ 满法一:数据块用尽。你存了个 100G 的大文件,
#   把空间塞满了 -> df -h 显示 100%。
# ★ 满法二:★ inode 用尽。你存了几百万个【小】文件,
#   空间没占多少,但 inode 一人一个,被领光了 ->
#   df -i 显示 100%。
# ★ 这两种"满",【任意一种发生,都建不了新文件】。
#   因为建一个文件,这两样【缺一不可】。

# === ★ 本文事故,就是第二种"满" ===
# ★ 580 万个小文件,数据块没怎么用(空间 58%),
#   但 inode 被吃光(100%)。仓库的"货架空间"还
#   很空,但"登记表的格子"已经一个不剩了。

# === 认知 ===
# ★ 一个文件系统有两种独立的、会被用尽的配额:数据块
#   (存文件内容,就是磁盘空间 GB)和 inode(存文件
#   元信息和索引,每个文件占恰好一个)。两者总量在
#   格式化时就定死、彼此独立。所以有两种"满":空间
#   满(df -h 100%)和 inode 满(df -i 100%)——
#   任意一种发生,都再也建不了新文件。

修复 2:inode 到底是什么,为什么它会单独耗尽

# === ★ 把 inode 这个概念,彻底讲透 ===

# === ★ inode = 文件的"身份证 + 索引卡" ===
# ★ 一个文件,在磁盘上其实是【两部分】:
#  - 内容:散落在若干个数据块里;
#  - inode:一张固定大小的"卡片",记着这个文件的
#    所有元信息(权限、属主、大小、三个时间戳),
#    以及【它的内容在哪几个数据块】的索引。
# ★ 注意:★ 文件名【不在 inode 里】!文件名记在
#   "目录"里,目录本质是一张"文件名 -> inode 号"
#   的对照表。inode 自己只有号码,没有名字。

# === ★ 看一个文件的 inode 号 ===
$ ls -i /etc/hosts
1234567 /etc/hosts                   # ★ 前面那个数字就是 inode 号
$ stat /etc/hosts                    # ★ 看 inode 里记的全部元信息
#  Inode: 1234567   Links: 1
#  Access/Modify/Change: ...

# === ★ 为什么 inode 会"单独"耗尽 ===
# ★ 因为:★ 不管文件多大多小,它都【恰好占一个 inode】。
#  - 一个 10GB 的大文件 -> 占 1 个 inode + 海量数据块
#  - 一个 1 字节的小文件 -> 占 1 个 inode + 1 个数据块
#  - 一个 0 字节的空文件 -> 占 1 个 inode + 0 个数据块!
# ★ 所以:文件【个数】决定 inode 用量,文件【大小】
#   决定数据块用量。这是两个【毫不相关】的维度。
# ★ 海量小文件 = 文件个数巨大、总大小很小
#   = inode 飞快耗尽、空间几乎没动。—— 本文场景。

# === ★ 一个反直觉的事实:空文件也会耗尽 inode ===
$ for i in $(seq 1 100000); do touch /tmp/test/f$i; done
# ★ 这创建了 10 万个 0 字节的文件。df -h 看,空间
#   几乎没变;df -i 看,inode 用量【涨了 10 万】。
# ★ "空文件不占地方"是错的 —— 它不占数据块,但它
#   照样占一个 inode。

# === ★ 目录,本身也占 inode ===
# ★ 目录也是一种"文件",它也占一个 inode。所以
#   "海量的小目录",同样会耗 inode。

# === ★ 谁规定了 inode 的总数 ===
# ★ 格式化时的 bytes-per-inode 比例决定。默认大约
#   每 16KB 空间分一个 inode。所以一个 100G 的盘,
#   inode 总数大约是 100G / 16KB ≈ 600 多万个。
# ★ 这个比例假设了"文件平均不会太小"。一旦你的
#   场景全是几 KB 的小文件,这个假设就被打破 ->
#   inode 会比空间【早得多】耗尽。

# === 认知 ===
# ★ inode 是文件的"身份证+索引卡",记权限、属主、
#   大小、时间戳和内容数据块的索引(文件名不在 inode
#   里,在目录那张"名字->inode 号"对照表里)。关键:
#   不管文件多大多小甚至 0 字节,都恰好占一个 inode
#   —— 文件【个数】决定 inode 用量,文件【大小】决定
#   空间用量,两者无关。海量小文件就会让 inode 先耗尽。

修复 3:df -i——看见那个被你忽略的维度

# === ★ 排查 No space,必须同时看两个 df ===

# === ★ df -h:看数据块(空间)用量 ===
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1       100G   58G   42G  58% /
# ★ 这是大家都熟的。Use% 是【空间】用了百分之几。

# === ★ df -i:★ 看 inode 用量 —— 多数人从不看的一栏 ===
$ df -i
Filesystem      Inodes   IUsed   IFree IUse% Mounted on
/dev/vda1      6553600 6553600       0  100% /
# ★ Inodes:这个文件系统 inode 的【总数】(格式化定的)
# ★ IUsed :已经用掉的(= 文件 + 目录的总个数)
# ★ IFree :还剩多少
# ★ IUse% :★ inode 用了百分之几 —— 这一栏 100%,
#   就是"建不了新文件"的真凶。

# === ★ 排查纪律:一报 No space,两个 df 都要敲 ===
# ★ No space left on device 这个错,有两个可能的真凶:
#  - df -h 的 Use% 是 100%  -> 真·空间满了
#  - df -i 的 IUse% 是 100% -> ★ inode 满了
# ★ 哪个 100%,就是哪个的问题。本文的我,只敲了
#   df -h,看见 58% 就懵了 —— 因为我漏看了 df -i
#   这个维度,而真凶恰恰藏在那里。
$ df -h; echo '---'; df -i            # ★ 养成两个一起敲的习惯

# === ★ 单独看某个文件系统 ===
$ df -ih /data                        # -i 和 -h 可一起,但读数看 IUse%
$ df -i /data                          # 只看 /data 的 inode

# === ★ 为什么"No space" 这个英文,会误导人 ===
# ★ 内核里,无论是数据块没了、还是 inode 没了,
#   底层返回的【是同一个错误码:ENOSPC】。
# ★ 而 ENOSPC 对应的那句人话,就是 "No space left
#   on device"。所以这句话,【同时承担了两种含义】,
#   它不告诉你到底是哪一种 —— 得你自己用 df -i 去分。

# === 认知 ===
# ★ df -h 只看数据块(空间)用量,df -i 才看 inode
#   用量(IUse% 那一栏)。No space left on device 这个
#   报错,底层是同一个错误码 ENOSPC,空间满和 inode
#   满【都报它】—— 它不告诉你是哪种。所以排查纪律是:
#   一报 No space,df -h 和 df -i 两个都敲,哪个 100%
#   就是哪个的问题。多数人只敲 df -h,会漏掉 inode。

修复 4:找出谁吃光了 inode

# === ★ 确认是 inode 满,下一步:揪出海量小文件在哪 ===

# === ★ 思路:inode 用量 = 文件个数,所以要找
#   "文件个数最多的目录" ===
# ★ 注意:不是找"最大的目录"(那是 du 干的事),
#   是找"文件【数量】最多的目录"。这俩是两回事。

# === ★ 方法一:逐个目录数文件个数,排序 ===
$ for d in /var/* /data/* /home/* /tmp/*; do
    n=$(find "$d" 2>/dev/null | wc -l)
    echo "$n $d"
  done | sort -rn | head
# 5800000 /data/msgcache             # ★★ 元凶:580 万个文件
# 120000  /var/spool/...
# ★ find 列出目录下所有文件和子目录,wc -l 数行数,
#   sort -rn 从多到少排。最上面那个,就是吃 inode 的。

# === ★ 方法二:从可疑大目录,逐层向下钻 ===
$ cd /data
$ for d in */; do echo "$(find "$d" | wc -l) $d"; done | sort -rn
# ★ 锁定 msgcache 后,再进去看它内部结构:
$ ls /data/msgcache | wc -l
5800000

# === ★ 几个最经常"偷偷攒出几百万文件"的地方 ===
# ★ 1. session 文件:PHP 的 /var/lib/php/session,
#   没配清理,会攒到几百万。
# ★ 2. 邮件队列:/var/spool/clientmqueue,程序
#   乱发邮件没人收,死信攒成山。
# ★ 3. 应用的临时 / 缓存目录:像本文的 msgcache,
#   程序只管写、不管删。
# ★ 4. 海量小日志、小图片缩略图。
# ★ 排查时,这几个地方优先看。

# === ★ 一个陷阱:目录里文件太多,ls 会卡死 ===
# ★ ls 默认要排序,几百万文件时它会吃光内存、卡很久。
$ ls -f /data/msgcache | head         # ★ -f 不排序,瞬间出结果
$ ls -1 /data/msgcache | wc -l        # 数个数也可能慢,要有耐心
# ★ 用 find 配合也行:
$ find /data/msgcache -maxdepth 1 -type f | wc -l

# === 认知 ===
# ★ 确认 inode 满后,要找的是"文件【个数】最多的
#   目录"(不是最大的目录)。用 for 循环对每个目录
#   find | wc -l 数文件数再 sort -rn 排序,最上面就是
#   元凶。常见偷偷攒几百万文件的地方:PHP session、
#   /var/spool 邮件队列、应用缓存临时目录。注意目录
#   文件极多时 ls 会卡死,要用 ls -f(不排序)。

修复 5:清理与根治——别只删一次了事

# === ★ 找到元凶后:怎么安全清掉,怎么不再犯 ===

# === ★ 第一步:确认这些小文件能不能删 ===
# ★ 先搞清楚 /data/msgcache 是干嘛的、那些 .tmp
#   是不是真的废弃临时文件。【确认无用】再删。
$ ls -f /data/msgcache | head         # 看文件长啥样
$ stat /data/msgcache/msg-0001.tmp    # 看时间戳,多老了

# === ★ 第二步:删海量小文件,别直接 rm * ===
# ★ rm /data/msgcache/* 会失败:参数太多,shell 报
#   "Argument list too long"。正确做法用 find -delete:
$ find /data/msgcache -type f -name '*.tmp' -delete
# ★ 或者,只删 7 天前的旧文件(更安全):
$ find /data/msgcache -type f -mtime +7 -delete
# ★ -mtime +7 = 修改时间在 7 天以前。边删边看 df -i,
#   IUse% 应该开始往下掉。

# === ★ 第三步:文件实在太多,删都删得慢 ===
# ★ 几百万文件,find -delete 也要跑很久。如果【整个
#   目录都能不要】,最快是把目录整个删了重建:
$ mv /data/msgcache /data/msgcache.old
$ mkdir /data/msgcache               # 立刻有个干净的新目录
$ rm -rf /data/msgcache.old &        # 后台慢慢删旧的
# ★ 这样服务能立刻恢复,删除在后台进行。

# === ★ 第四步:★ 根治 —— 不能只删一次 ===
# ★ 只删一次,过几个月又会满。必须根治【为什么会攒
#   出几百万文件】:
# ★ 根治 a:让产生文件的程序,自己定期清理 / 设上限。
# ★ 根治 b:加一个定时清理任务,自动删旧文件:
$ crontab -e
0 4 * * * find /data/msgcache -type f -mtime +3 -delete
# ★ 根治 c:如果是 session、邮件队列,去配置它们
#   自带的过期清理机制。

# === ★ 第五步:如果场景就是天生"海量小文件" ===
# ★ 有些业务,注定要存几千万小文件。这种,要在
#   【格式化时】就调高 inode 密度:
$ mkfs.ext4 -i 4096 /dev/vdb1         # ★ -i 4096:每 4KB 一个 inode
# ★ 默认每 16KB 一个 inode,改成 4KB,inode 总数
#   翻 4 倍。★ 但这要【重新格式化】,是建盘时的决策,
#   线上盘改不了 —— 所以建盘前就要预估好文件特征。
# ★ 或者改用对小文件更友好的文件系统 / 对象存储。

# === 认知 ===
# ★ 清理:确认小文件无用后,用 find -delete(别用
#   rm * 会 Argument list too long),可配 -mtime +7
#   只删旧的;整个目录能弃就 mv 走再后台 rm -rf,
#   服务立刻恢复。★ 但只删一次没用,必须根治:让
#   程序自己清理、加 cron 定时清理。天生海量小文件的
#   场景,要在 mkfs 时用 -i 调高 inode 密度(需重新
#   格式化,是建盘决策)。

修复 6:磁盘空间与 inode 排查纪律

# === 这次事故暴露的认知盲区,定几条纪律 ===

# === 1. ★ 报 No space left on device,df -h 和 df -i 必须都敲 ===
$ df -h ; echo '---' ; df -i

# === 2. ★ 一个文件系统有两种配额:数据块(空间)和 inode(文件个数)===

# === 3. ★ 每个文件占恰好一个 inode,不论它多大,哪怕 0 字节 ===

# === 4. ★ 空间没满 inode 满了,就是海量小文件 —— 去找文件个数最多的目录 ===
$ for d in /var/* /data/*; do echo "$(find $d 2>/dev/null|wc -l) $d"; done|sort -rn|head

# === 5. 删海量文件用 find ... -delete,别用 rm *(Argument list too long)===

# === 6. ★ 目录文件极多时 ls 会卡死,用 ls -f 不排序 ===

# === 7. 整个目录能弃,就 mv 走再后台 rm -rf,服务立刻恢复 ===

# === 8. ★ 只删一次会复发,必须根治:程序自清理 + cron 定时清理 ===

# === 9. 天生海量小文件的盘,mkfs 时用 -i 调高 inode 密度(建盘决策)===

# === 10. 排查 No space left on device 的步骤链 ===
$ df -h                              # ① 空间满了吗
$ df -i                              # ② ★ inode 满了吗
$ lsof | grep deleted                # ③ 已删文件句柄没放?(另一种)
$ for d in ...; do find $d|wc -l; done | sort -rn  # ④ 谁吃的 inode
# df -h 满 -> 清大文件;df -i 满 -> 清海量小文件并根治。

命令速查

需求                        命令
=============================================================
看磁盘空间用量              df -h
★ 看 inode 用量             df -i
看某文件的 inode 号         ls -i 文件
看某文件 inode 全部元信息   stat 文件
数目录下文件个数            find 目录 | wc -l
列超多文件的目录(不卡)    ls -f 目录 | head
找文件个数最多的目录        for d in /*; do echo "$(find $d|wc -l) $d"; done|sort -rn
删海量小文件                find 目录 -type f -delete
只删 7 天前的旧文件         find 目录 -type f -mtime +7 -delete
看已删除但句柄没放的文件    lsof | grep deleted
建盘时调高 inode 密度       mkfs.ext4 -i 4096 /dev/xxx

口诀:No space 有两种 空间满看 df -h inode 满看 df -i 两个都要敲
      每个文件不论大小占一个 inode 海量小文件耗的是 inode 不是空间

避坑清单

  1. 报 No space left on device 不要只敲 df -h,必须同时敲 df -i 看 inode 用量
  2. 一个文件系统有两种独立配额:数据块就是磁盘空间 GB,inode 存文件元信息每个文件占一个
  3. 每个文件不论多大哪怕 0 字节空文件,都恰好占用一个 inode,目录本身也占一个
  4. 文件个数决定 inode 用量,文件大小决定空间用量,这是两个毫不相关的维度
  5. 空间没满但 inode 满了,根因必是海量小文件,要去找文件个数最多而不是最大的目录
  6. No space left on device 底层是同一个错误码 ENOSPC,空间满和 inode 满都报它,它不区分
  7. 删海量小文件用 find 目录 -delete,别用 rm * 会报 Argument list too long
  8. 目录里文件几百万时 ls 会因排序卡死吃内存,要用 ls -f 不排序
  9. 只清理一次过几个月还会满,必须根治让程序自己清理并加 cron 定时清理任务
  10. 天生要存海量小文件的盘,要在 mkfs 格式化时用 -i 参数调高 inode 密度,这是建盘决策线上改不了

总结

这次"磁盘有空间却报没空间"的事故,纠正了我一个关于"满"的、藏得极深的错觉。在我过去的脑子里,一块磁盘的状态,是【一根单一的、连续的标尺】。这根标尺从 0% 到 100%,我用 df -h 一看,指针停在哪,磁盘就"满"到哪。"磁盘满了"这件事,在我心里是【一维】的——它只有一个量、一个百分比、一个真相。所以当服务喊"没空间"时,我笃定地认为,我只要找到那根标尺,看一眼那个逼近 100% 的数字,事情就破了。我敲下 df -h,准备和那个 100% 打个照面——结果它回了我一个 58%。那一刻我是真的懵了,因为这个 58%,在我那套"一维标尺"的世界观里,是一个【自相矛盾】的东西:标尺明明还没满,系统却说满了。我甚至开始怀疑是不是系统在撒谎、是不是 df 坏了。直到 df -i 那一行 100% 跳出来,我那根"单一标尺"才"啪"地一声断成了两根。我这才被逼着看清一件我用了这么多年 Linux 却从没正视过的事:一块磁盘的"满",从来【不是一维的】。它身上,有【两根】各自独立的标尺——一根量"空间",装文件的肉;一根量"inode",装文件的数。这两根标尺,刻度不同、用尽的节奏也不同,谁先到 100%,完全取决于你往里塞的是什么:你塞几个巨大的文件,空间那根先到顶;你塞几百万个芝麻小文件,inode 那根先到顶。而那句让我困惑了半天的 No space left on device,它最"坏"的地方,就在于它用了一个【单数】的、含混的词——"space"。它让我以为只有一种"space",于是我只去看那一根标尺。可内核说"没 space 了"时,它心里指的,可能是【这两根标尺中的任意一根】。我和系统之间,从来没有谁撒谎——我们看的是同一块磁盘;我们的分歧,只是我手里攥着一根标尺,而它说的是另一根。复盘到根上我才明白,我犯的错,是把一个【多维】的资源,在脑子里【降维】成了一维。我以为"磁盘够不够用"是一个问题,其实它是两个:"装得下这些内容的体积吗"和"登记得下这些文件的数量吗"。这次最大的收获,是我对一切"资源够不够"的判断,都生出了一种新的警觉:在我说"它还够用"之前,我必须先问一句——我衡量的,是它的【哪一个维度】?这个东西,会不会还有【我没在看的、另一个维度】,早已悄悄见底?一个数据库,我看了连接数,内存呢、磁盘 IO 呢、表锁呢?一个线程池,我看了线程数,队列长度呢、任务堆积呢?一台机器,我看了 CPU,内存、文件描述符、网络带宽呢?这些东西,没有一个是"一根标尺"能说清的。它们都像那块磁盘——是一捆【捆在一起、却各自独立到顶】的标尺,而系统报警时,往往只给你一句含混的、单数的"满了 / 不够了",绝不会贴心地告诉你是哪一根先断的。所以,以后任何一个"为什么明明没满却出问题"的困惑,我都会先停下来,不去怀疑系统,而是怀疑自己:我是不是,又只盯着那一根我最熟悉的标尺,而把真正先断掉的那一根,漏在了视野之外?把多维的东西看成一维,是最舒服、也最危险的偷懒——磁盘这件事教得最透:它从来不是一根标尺,它是一捆。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
Linux教程

脚本手动跑正常放进 crontab 就 command not found:一次环境变量加载顺序的复盘

2026-5-21 1:07:51

Linux教程

SSH 报 REMOTE HOST IDENTIFICATION HAS CHANGED:一次主机密钥与中间人警告的复盘

2026-5-21 1:15:48

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索