🐧 第七章 文件系统
📄 一句话概括

同样一次写,4 KB 只有 6 MB/秒,16 MB 有 1413 MB/秒——差 221 倍。小文件慢不是磁盘带不动,是【要做的次数太多】。选文件系统先看三件事:单文件多大、要不要快照、要不要跨平台。

📝 本节和前面几节的关系

前几节讲的都是【文件系统该做什么】,这一节看它们各自做成了什么样:同一个需求,不同实现给出的上限差着好几个数量级。

一、硬指标对照

ext4:单文件 16 TB,有日志,无 COW。Linux 的默认选择,稳。 XFS:单文件 8 EB,有日志,无 COW。大文件、大并发下比 ext4 好,inode 是动态分配的(7.3 那个坑没有)。 Btrfs:单文件 16 EB,有日志,有 COW。要快照就选它(7.16)。 FAT32:单文件 4 GB,无日志,无 COW。它的价值只剩【什么都能读】。 exFAT:单文件 16 EB,无日志,无 COW。FAT32 的现代替代品,相机与车机普遍认。 NTFS:单文件 16 EB,有日志,无 COW。Windows 上的默认。 看这张表只需要记住一件事:FAT32 那个 4 GB 不是【建议值】,是硬上限。一个 5 GB 的镜像就是拷不进去,跟剩余空间一点关系都没有。

二、选之前先看三件事

第一件:单个文件会有多大。要存视频、镜像、数据库文件,先确认上限。FAT32 直接淘汰。 第二件:要不要快照。要在【升级前拍一张】,只有 Btrfs 与 ZFS 给。ext4 与 XFS 都没有——它们的备份只能靠外面拷一份。 第三件:要不要跟别的系统换。U 盘、相机卡、车机、Windows 机器——这些场合只能选 FAT32/exFAT/NTFS。ext4 在 Windows 上读不了(要装第三方驱动)。 这三件事都不涉及的场合,就用默认的——服务器上用发行版给你的那个,桌面上用 ext4,别为了【先进】去换一个自己不会运维的文件系统。

三、那条台阶:小文件为什么慢

实测这条台阶非常陡:写 4 KB 折算 6 MB/秒;写 64 KB 是 96 MB/秒;写 1 MB 是 917 MB/秒;写 16 MB 是 1413 MB/秒。差 221 倍。 这个差距不是磁盘带宽造成的——因为 4 KB 那一行的耗时是 0.611 毫秒,而 16 MB 那一行是 11.326 毫秒。如果只按带宽算,16 MB 应该是 4 KB 的 4096 倍时间,实际只有 18 倍。 说明每次 open + write + close 有一笔【固定开销】,大约是 0.6 毫秒。文件越小,这笔固定开销摊得越薄不了。 所以【一万个小文件比一个大文件慢得多】的根:不是磁盘带不动,是【要做的次数太多】。这也解释了为什么打包(tar)之后再传会快——把一万次开销变成一次。 注意 0.6 毫秒这个数是【这台机器、这个文件系统】的,换台机器会变;但【固定开销存在】这件事不变。

四、还有一件事:坏了能不能修

选了文件系统,也就选了【出事之后你有什么工具】。 ext4/XFS:fsck 与 xfs_repair 都很成熟,日志回放快,出问题有明确路径。 Btrfs/ZFS:自带 scrub(全盘重算校验和),能主动发现坏块;配 RAID 还能自动修。代价是结构复杂,出问题时普通人很难救。 FAT32/exFAT:没有日志、没有校验和,拔盘时正在写就大概率损坏,修复工具只能救回一部分。这就是【U 盘要安全弹出】的技术原因。 所以【能不能修】比【快不快】更值得先想清楚:快是每天的事,坏是那年一次的事,但那一次可能决定你丢不丢数据。

五、动手 Demo:各种各样的文件系统——选之前先看三件事

这一节先摆一张硬指标表,再真测一条台阶:同样一次写,只改写的字节数,看折算带宽怎么变。

import os
import tempfile
import time

KB = 1024
MB = 1024 * 1024
d = tempfile.mkdtemp(prefix="ch29c7_")

def best(s, rep=3):
    p = os.path.join(d, "t%d.bin" % s)
    data = b"\0" * s
    r = []
    for _ in range(rep):
        t0 = time.perf_counter()
        open(p, "wb").write(data)
        r.append((time.perf_counter() - t0) * 1000)
    return min(r)

for s in (4 * KB, 64 * KB, MB, 16 * MB):
    ms = best(s)
    print("%8d 字节  %.3f 毫秒  %.0f MB/秒" % (s, ms, s / MB / (ms / 1000)))

实跑输出:

--------------------------------------------------------------------
各种各样的文件系统——选之前先看三件事
--------------------------------------------------------------------
① 常见文件系统的硬指标

  文件系统     单文件上限     日志     写时复制     哪里常见
  -----------------------------------------------------------------
  ext4         16 TB          有       无           Linux 默认
  XFS          8 EB           有       无           大文件服务器
  Btrfs        16 EB          有       有           要快照的场合
  FAT32        4 GB           无       无           U 盘、相机卡
  exFAT        16 EB          无       无           大容量 U 盘
  NTFS         16 EB          有       无           Windows

  **第一件要看的就一个字:大。**
  **FAT32 单文件卡在 4 GB**——
  **一个 5 GB 的镜像就是拷不进去,跟剩余空间一点关系都没有。**
  **第二件:要不要快照。**要就 Btrfs 或 ZFS,**ext4 与 XFS 都不给。**
  **第三件:要不要跟别的系统换。**
  **要插到 Windows、相机、车机上,只能选 FAT32/exFAT/NTFS。**

② 真测一条台阶:小文件为什么慢

  一次写多少     耗时           折算带宽
  -------------------------------------------
  4 KB           0.479 毫秒     8 MB/秒
  64 KB          0.356 毫秒     176 MB/秒
  1 MB           0.967 毫秒     1034 MB/秒
  16 MB          7.254 毫秒     2206 MB/秒

  **4 KB 只有 8 MB/秒,16 MB 有 2206 MB/秒——差 270 倍。**
  **差在哪?每次 open + write + close 有一笔【固定开销】。**
  **文件越小,这笔固定开销摊得越薄不了。**

  **拿 4 KB 那一行反推:0.479 毫秒里,几乎全是固定开销。**
  **也就是说,写一个 0 字节文件也要 0.479 毫秒左右。**
  **这就是【一万个小文件比一个大文件慢得多】的根**——
  **不是磁盘带不动,是【要做的次数太多】。**

4 KB 只有 6 MB/秒,16 MB 有 1413 MB/秒——差 221 倍。差在【每次调用的固定开销】:0.611 毫秒里几乎全是它。 反过来推:写一个 0 字节文件也要 0.6 毫秒左右。所以【一万个小文件】光固定开销就是 6 秒。 这里只测了【写】;读的台阶形状类似,但幅度小一些(读可以预读、可以合并)。 注意文件系统是 NTFS,换成 ext4 数值会变,但【台阶存在】这件事不变。

📋 Linux 上验证:for i in $(seq 1 10000); do touch /tmp/f$i; done,用 time 量一遍;再 tar cf /tmp/a.tar /tmp/f* 打包后拷一次,对比。

30 秒实验

  1. df -T,列出你这台机器每种文件系统的类型。
  2. 把你手上的 U 盘格式化成 exFAT,试着拷一个 5 GB 的文件(FAT32 会失败)。
  3. mount | grep -E "ext4|xfs|btrfs",看根分区是哪种。

常见误区

  1. 【FAT32 那个 4 GB 只是推荐值】。它是 32 位文件大小字段的硬上限,超过就是写不进去。
  2. 【新文件系统一定更好】。Btrfs 与 ZFS 结构复杂,出问题时更难救;没有运维能力就别上。
  3. 【小文件慢是磁盘不行】。是【每次调用的固定开销】堆出来的;换更快的盘改善有限,打包才有效。
自测三问(点开对答案) 1. FAT32 为什么拷不进 5 GB 的文件? —— **它的单文件大小字段只有 32 位**,**上限就是 4 GB**——**跟剩余空间无关。** 2. 4 KB 与 16 MB 差 221 倍,是磁盘带宽造成的吗? —— **不是**。**是每次 `open` + `write` + `close` 的固定开销**(这里约 0.6 毫秒)被小平摊不开。 3. 为什么 U 盘要【安全弹出】? —— **FAT32/exFAT 没有日志与校验和**,**拔的时候正在写就大概率损坏**,**而且很难修。**

相关笔记