1 万个 1 字节的文件,占了我 40 MB 硬盘

经验 2026-11-06 20:05 · 10 次阅读 ·磁盘,小文件,inode,打包

我在服务器上建了 1 万个空文件,每个只有 1 个字节。硬盘因此少了 40 MB。

不是文件本身大。硬盘存东西是按格子给的,一格 4 KB,文件再小也要占满一整格。像公交车座位,不管坐一个人还是坐满,那一排都归你,别人上不来。

同样 10 MB 内容,1 万个文件占盘 40 MB,一个大文件只占 11 MB

背景

我在整理几个目录,顺手比了一下大小,发现对不上。

sun-style 这个目录,看内容是 45 KB,看实际占用是 360 KB,差了 8 倍。main-site 是 733 MB 对 735 MB,几乎一样。

差的这 8 倍不在文件里,在文件的数量上。于是我想把这件事量清楚:多小的文件最吃亏,多到什么程度会开始拖慢操作。

先看这台机器的情况:

$ find /home/ubuntu -xdev -type f | wc -l
447703
$ find /home/ubuntu -xdev -type f -size -4k | wc -l
287390

44.7 万个文件,28.7 万个不到 4 KB,占了六成多。

实测一:1 万个文件吃多少硬盘

先读一次硬盘已用空间,sync 把缓存写下去,再建 1 万个文件,然后再读一次,两次相减就是真实增量。

我做了三组:每个文件 1 字节、每个 1 KB、以及一个 10.24 MB 的大文件。

造法 内容大小 实际占盘 建好耗时
1 万个 1 字节文件 9.8 KB 40 MB 0.48 秒
1 万个 1 KB 文件 9.8 MB 40 MB 0.60 秒
1 个 10.24 MB 文件 10 MB 11 MB 0.02 秒

第一行最刺眼:内容 9.8 KB,占盘 40 MB,放大了四千倍。

第二行是日常最常遇到的:内容 10 MB,占盘 40 MB,四倍。照片缩略图、聊天记录、网页缓存、代码里的依赖包,都在这条线上。

第三行反过来看:一个大文件装 10 MB 内容,只多占不到 1 MB。同样十兆内容,装法不同,占盘差 3.6 倍。

这里有个容易看错的地方。du -sh 目录 显示的是占盘大小,du -sh --apparent-size 目录 才是内容大小。我一开始只看了前者,还以为文件坏了。

实测二:复制、删除、打包差多少

占盘只是一半。另一半是时间。下面每项都跑三次取中间值,每次测之前先把文件重建好。

操作 1 万个 1 KB 小文件 1 个 10.24 MB 文件
复制(cp) 631 毫秒 30 毫秒
删除 210 毫秒 2 毫秒
打包成 tar.gz 229 毫秒 —
解包 532 毫秒 —

复制差了二十倍。这些数据都在内存缓存里,比的是"文件个数"本身的开销,不是硬盘慢。硬盘真转起来,差距只会更大。

打包的结果更直接:1 万个 1 KB 的文件占盘 40 MB,压成一个包只有 154,249 字节,也就是 150 KB。

所以"先打包再搬"这件事成立:搬 150 KB 比搬 1 万个文件省事,代价是两头各花半秒。

tar -czf 打包.tar.gz 目录名/
tar -xzf 打包.tar.gz -C 目标目录/

结论与踩坑

先看自己哪里小文件最多。这条命令只数,不动任何东西:

find 目录 -type f -size -4k | wc -l

数字上万,就该考虑打包。我这边 28.7 万个,说明这现象到处都是。

再看 du,记得加参数:

du -sh --apparent-size 目录    # 内容多大
du -sh 目录                    # 占盘多少

两个数差得越多,说明这个目录里小文件越多。

踩到的三个坑。

第一个:我以为 rsync 比 cp -r 快,实测反过来。同样 1 万个 1 KB 文件,cp -r 631 毫秒,rsync -a 1234 毫秒,慢一倍。它要多算校验、走自己的传输协议。断点续传、只传改动过的部分才是它的长处,单次整目录复制不是。

第二个:第一次测删除,我得到一组 2.2 毫秒的数字。那是假的——脚本循环跑两次,第二次文件已经删掉了,rm 找不到东西,当然快。改成每轮先重建再删,数字才稳定。同一个动作重复测,每一轮都要把现场恢复成一样。

第三个:建这 2 万个文件,除了占盘,还占掉 20,013 个文件编号(inode,硬盘给每个文件发的号)。本机总共 327 万个编号,已用 64.8 万个。空间用光报"设备上没有空间",编号用光报的是同一句话,很难分清。文件特别多的时候,两样都要看。

继续看

全部 →