为什么 du 和 ls 报告的文件大小之间存在巨大差异?

Dmi*_*sky 2 linux file-management filesystems tar

在服务器上,我有一个目录/opt/kafka/data/topics.

$ du -hs /opt/kafka/data/topics
52M     /opt/kafka/data/topics
Run Code Online (Sandbox Code Playgroud)

当我 tar 这个目录像

$ tar czfv /tmp/topics.tar.gz /opt/kafka/data/topics
Run Code Online (Sandbox Code Playgroud)

我得到一个有意义的文件大小

$ ls -alh /tmp/topics.tar.gz
-rw-r--r-- 1 user user  11M Jan 12 15:15 kafka
Run Code Online (Sandbox Code Playgroud)

但是,当我下载topics.tar.gz到本地 OS X 计算机并解压缩时,它占用了 10GB!


/opt/kafka/data/topics更仔细地检查服务器上的内容后,我注意到ls其中包含许多 10MB 的文件:

$ find /opt/kafka/data -type f -exec ls -alh {} \;
... [output]
-rw-r--r-- 1 user user 10M Jan 12 02:45 /opt/kafka/data/topics/user-entities-KTABLE-REDUCE-STATE-STORE-0000000178-changelog-1/00000000000000000000.index
-rw-r--r-- 1 user user 10M Jan 12 02:45 /opt/kafka/data/topics/user-entities-KSTREAM-KEY-SELECT-0000000123-repartition-2/00000000000000000012.index
... [and many more]
Run Code Online (Sandbox Code Playgroud)

du 报告这些 10MB 文件中的每一个都是 0 字节:

$ du -h /opt/kafka/data/topics/user-entities-KTABLE-REDUCE-STATE-STORE-0000000178-changelog-1/00000000000000000000.index
0       /opt/kafka/data/topics/user-entities-KTABLE-REDUCE-STATE-STORE-0000000178-changelog-1/00000000000000000000.index
Run Code Online (Sandbox Code Playgroud)

那么发生了什么?显然我在这里遗漏了一些东西:

  • du报告总数为 5200 万。这是有道理的,因为/opt/kafka/data安装的设备只有 5GB,df报告说它只有 2% 已满并且一切仍在工作。
  • tar将内容 gzip 压缩到 10M。这也有道理。
  • ls 报告说,许多文件在磁盘上为 10M,当我提取存档时,我得到 10GB。
  • du 报告这些相同的文件中的每一个都是 0 字节。
  • mount 报道说 /dev/sdc on /opt/kafka/data type ext4 (rw,relatime,data=ordered)

什么都没有加起来。是否有某种我不知道的透明磁盘压缩?

Aus*_*arn 5

根据评论中的讨论,所有文件都是稀疏的。这种事情其实很多人在第一次处理的时候都会感到困惑,所以不要难过。

ls和报告的值实际上发生了什么du

用一个例子最容易解释这一点。

假设您创建了一个空文件,然后从头开始向其中写入 1MB 的数据。生成的文件大小为 1MB,在磁盘上占用 1MB。双方lsdu会报告该文件相同的大小1MB。

现在假设您创建一个空文件,然后调用seek()将 1MB 移动到文件中,然后写入一个字节。生成的文件看起来是 1MB + 1 字节长,但实际上只有 1 字节的数据。

在较旧的文件系统上,第二个文件将花费很长时间来写入那 1 个字节的数据,因为操作系统会在写出最后 1 个字节的实际数据之前忙着写出 1MB 的空字节。

这种低效率(在创建文件的时间和磁盘上使用的空间方面)是稀疏文件的来源。而不是写出 1MB 的空字节,支持稀疏文件的操作系统(如所有现代 UNIX 系统)将在该文件系统的元数据中注释 0-1MB 区域为空,然后仅存储您写入的单个字节。结果,文件看起来是 1MB + 1 字节长,但在磁盘上它只会占用 1 字节。此外,当读取该文件时,操作系统注释为空的任何区域都将作为空字节读回(因此它与第一个文件中的用户程序看起来没有什么不同)。

这就是报告的值lsdu来自的值之间的差异。默认情况下,ls报告文件的表观大小(即,如果您从第一个字节开始读取文件并一直读取到最后,您将读取多少数据),同时du报告文件在磁盘上使用的实际空间(通常不包括由操作系统完成的其他节省空间的技巧,如透明压缩)。 在这种情况下du同意,df因为df只报告磁盘上实际实际使用的空间量。

通过将该ls -l命令更改为ls -ls,您将获得一个额外的列,显示文件的实际磁盘大小,这应该与du.