mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1989 字
5 分钟
文件系统
2025-05-01

执行 rm 删除一个 10GB 的文件,磁盘空间不会立刻减少——因为数据还在盘面上,只是 inode 的引用被清除了。断电后重启,有的文件完好无损,有的却变成零字节——文件系统的日志模式决定了哪些数据能活下来。在数据库场景下,ext4 的 data=ordereddata=journal 之间,性能差距可达 2 倍。

文件系统是磁盘与数据库之间的关键抽象层。ext4、XFS、Btrfs 三大主流文件系统各有取舍——理解它们的设计,才能为存储引擎选对地基。

一、文件系统基础#

1.1 文件系统的核心职责#

graph TB subgraph 文件系统职责 ORG["数据组织<br/>如何将文件映射到磁盘块"] CONSIST["崩溃一致性<br/>断电后如何保证数据完整"] SPACE["空间管理<br/>如何分配和回收磁盘块"] PERM["权限与安全<br/>访问控制、ACL"] JOURNAL["日志机制<br/>快速恢复"] end style ORG fill:#e3f2fd,stroke:#1565c0 style CONSIST fill:#c8e6c9,stroke:#2e7d32 style SPACE fill:#fff9c4,stroke:#f9a825
职责说明对数据库的影响
数据组织文件→块的映射方式顺序读写性能
崩溃一致性断电后的数据完整性数据丢失风险
空间管理块的分配与回收碎片化、写放大
元数据管理inode、目录项、扩展属性文件操作延迟
日志机制记录元数据/数据变更崩溃恢复速度

1.2 文件系统的核心概念#

// 文件系统的核心数据结构
struct Superblock {
uint32_t s_inodes_count; // inode 总数
uint32_t s_blocks_count; // 块总数
uint32_t s_free_blocks; // 空闲块数
uint32_t s_log_block_size; // 块大小(1KB << s_log_block_size)
uint32_t s_blocks_per_group; // 每组块数
char s_uuid[16]; // 文件系统 UUID
};
struct Inode {
uint16_t i_mode; // 文件类型与权限
uint32_t i_size; // 文件大小
uint32_t i_atime; // 访问时间
uint32_t i_mtime; // 修改时间
uint32_t i_ctime; // 状态变更时间
uint32_t i_blocks[15]; // 数据块指针(直接/间接)
// ext4: i_block 变为 extent 树
};
概念说明大小
超级块(Superblock)文件系统全局信息~1KB
inode文件元数据128–256B
数据块(Block)文件内容存储单元1KB/2KB/4KB
块组(Block Group)块的分组管理单元8192 块(4KB 块时 32MB)
目录项(Dentry)文件名→inode 映射可变长
Extent连续块的描述(替代间接块)12B/个

1.3 从间接块到 Extent#

传统 Unix 文件系统使用直接/间接块指针:

graph TB subgraph 间接块方案["传统间接块方案"] INODE1["inode"] --> D0["直接块 0-11<br/>48KB 数据"] INODE1 --> IND1["一级间接块<br/>1024 个指针"] IND1 --> DATA1["数据块"] INODE1 --> DIND1["二级间接块"] DIND1 --> IND2["一级间接块"] IND2 --> DATA2["数据块"] end subgraph Extent方案["Extent 方案(ext4)"] INODE2["inode"] --> EXT0["Extent 0<br/>起始块+长度<br/>连续 48KB"] INODE2 --> EXT1["Extent 1<br/>起始块+长度<br/>连续 1GB"] INODE2 --> EXT_TREE["Extent 树节点<br/>更大的文件"] end style 间接块方案 fill:#ffe0b2,stroke:#e65100 style Extent方案 fill:#c8e6c9,stroke:#2e7d32
方案小文件大文件碎片化遍历效率
间接块快(直接指针)慢(多次间接)严重O(深度)
Extent快(内联 Extent)快(Extent 树)较少O(log N)

二、ext4 文件系统#

2.1 ext4 架构#

graph TB subgraph ext4["ext4 文件系统架构"] SB["超级块<br/>全局信息"] BG0["块组 0<br/>超级块+GDT+inode位图+块位图+inode表+数据"] BG1["块组 1<br/>GDT+位图+inode表+数据"] BGN["块组 N<br/>..."] SB --> BG0 --> BG1 --> BGN subgraph 块组内部 GDT["块组描述符 GDT"] IBMP["inode 位图"] BBMP["块位图"] ITABLE["inode 表"] DATA["数据块"] end end style SB fill:#e3f2fd,stroke:#1565c0 style BG0 fill:#c8e6c9,stroke:#2e7d32

ext4 的关键特性:

特性说明优势
Extent连续块描述,替代间接块减少元数据开销,提升大文件性能
日志(JBD2)记录元数据/数据变更崩溃后快速恢复
延迟分配写入时暂不分配块,缓存后批量分配减少碎片,提升顺序性
多块分配一次分配多个连续块减少碎片
大文件支持最大 16TB(4KB 块)适合大文件场景
无日志模式可关闭日志提升写入性能,降低安全性

2.2 ext4 日志模式#

ext4 支持三种日志模式:

模式记录内容安全性性能适用场景
journal元数据 + 数据最高最低数据安全要求极高
ordered(默认)仅元数据,数据先写通用场景
writeback仅元数据,数据不保证顺序最高临时数据、缓存
# 查看当前日志模式
dumpe2fs /dev/sda1 | grep "Filesystem features"
# 或
cat /proc/mounts | grep ext4
# 设置日志模式
mount -o data=journal /dev/sda1 /mnt/data # 最安全
mount -o data=ordered /dev/sda1 /mnt/data # 默认
mount -o data=writeback /dev/sda1 /mnt/data # 最快
# 在 /etc/fstab 中配置
# /dev/sda1 /mnt/data ext4 defaults,data=ordered 0 0
Important

数据库通常使用 data=ordered 模式,并在数据库层面自行保证数据一致性(WAL)。不建议使用 data=writeback,因为文件系统可能在崩溃后丢失最近写入的数据。

2.3 ext4 对数据库的影响#

# 数据库推荐的 ext4 挂载选项
mount -o noatime,nodiratime,data=ordered,barrier=1 \
/dev/sda1 /var/lib/mysql
# 各选项说明:
# noatime — 不更新访问时间,减少元数据写入
# nodiratime — 不更新目录访问时间
# data=ordered — 默认日志模式
# barrier=1 — 启用写屏障,保证写入顺序(重要!)
挂载选项说明对数据库的影响
noatime不更新 atime减少约 5–10% 的元数据写入
nodiratime不更新目录 atime进一步减少写入
data=ordered默认日志模式平衡安全与性能
barrier=1启用写屏障保证 WAL 写入顺序,必须开启
nobarrier禁用写屏障危险!可能导致 WAL 失效

三、XFS 文件系统#

3.1 XFS 架构#

graph TB subgraph XFS["XFS 文件系统架构"] AG0["分配组 AG 0<br/>超级块+空闲空间+BTree+inode"] AG1["分配组 AG 1<br/>..."] AGN["分配组 N<br/>..."] subgraph AG内部["分配组内部"] SB_XFS["超级块"] AGF["空闲空间 B+ 树<br/>按大小索引"] AGI["inode B+ 树<br/>按编号索引"] AGFL["空闲列表<br/>快速分配"] end AG0 --> AG1 --> AGN end style AG0 fill:#e3f2fd,stroke:#1565c0 style AG1 fill:#c8e6c9,stroke:#2e7d32

XFS 的核心设计思想是分配组(Allocation Group, AG)——将文件系统划分为多个独立的分配区域,每个 AG 有自己的空间管理和 inode 管理:

特性说明优势
分配组文件系统划分为多个独立 AG并行 I/O、可扩展
B+ 树索引空闲空间和 inode 用 B+ 树管理高效查找
延迟分配类似 ext4,但实现更激进更少的碎片
预分配支持空间预分配减少碎片
大文件支持最大 8EB超大文件
在线扩容支持在线增大文件系统运维友好
元数据日志仅记录元数据高性能

3.2 XFS vs ext4#

维度ext4XFS
最大文件系统16TB(4KB 块)8EB
最大文件16TB8EB
并行 I/O有限(单锁)好(多 AG)
大文件性能优秀
小文件性能优秀一般
删除大文件慢(需遍历 Extent)
缩容支持不支持
默认日志模式ordered元数据日志
数据库推荐MySQL/PostgreSQL大文件/数据仓库

3.3 XFS 对数据库的优化#

# 数据库推荐的 XFS 挂载选项
mkfs.xfs -f -b size=4096 -d su=64k,sw=4 /dev/sda1
mount -o noatime,nodiratime,logbufs=8,logbsize=256k \
/dev/sda1 /var/lib/mysql
# 各选项说明:
# -b size=4096 — 块大小 4KB
# -d su=64k,sw=4 — 条带单元 64KB,条带宽度 4(RAID 优化)
# logbufs=8 — 日志缓冲数量
# logbsize=256k — 日志缓冲大小

四、Btrfs 文件系统#

4.1 Btrfs 架构#

graph TB subgraph Btrfs["Btrfs 文件系统架构"] subgraph 核心特性 COW["CoW 写时复制<br/>所有写入都是 CoW"] SNAP["快照<br/>瞬间创建,零开销"] SUBVOL["子卷<br/>独立的文件树"] COMPRESS["透明压缩<br/>LZO/ZSTD/LZ4"] RAID_BTRFS["内置 RAID<br/>RAID 0/1/5/6/10"] CHECKSUM["数据校验<br/>CRC32C"] end COW --> SNAP COW --> CHECKSUM SUBVOL --> SNAP end style COW fill:#e3f2fd,stroke:#1565c0 style SNAP fill:#c8e6c9,stroke:#2e7d32 style SUBVOL fill:#fff9c4,stroke:#f9a825

Btrfs 的核心设计思想是 CoW(Copy-on-Write)——所有写入都创建新的数据块,而不是原地更新:

// Btrfs CoW 写入流程(简化)
void btrfs_write(struct btrfs_fs *fs, u64 ino,
u64 offset, void *data, int len) {
// 1. 分配新的数据块(不覆盖旧块)
u64 new_bytenr = allocate_chunk(fs);
// 2. 写入数据到新块
write_data(new_bytenr, data, len);
// 3. 更新 extent 树(记录新块的引用)
update_extent_tree(fs, new_bytenr, len);
// 4. 更新文件树的叶子节点(CoW 路径)
update_file_tree(fs, ino, offset, new_bytenr, len);
// 这一步也会触发 CoW:从叶子到根的路径都需要复制
// 5. 更新超级块(原子切换根节点)
commit_super(fs);
// 6. 旧块变为可回收
// 不会被立即删除,快照可能还在引用
}

4.2 Btrfs 子卷与快照#

# 创建子卷
btrfs subvolume create /mnt/data/mysql
btrfs subvolume create /mnt/data/postgres
btrfs subvolume create /mnt/data/backup
# 查看子卷
btrfs subvolume list /mnt/data
# 创建快照(瞬间完成,零额外空间)
btrfs subvolume snapshot /mnt/data/mysql /mnt/data/mysql-snap-$(date +%Y%m%d)
# 从快照恢复
btrfs subvolume delete /mnt/data/mysql
btrfs subvolume snapshot /mnt/data/mysql-snap-20260601 /mnt/data/mysql
# 设置子卷为默认挂载
btrfs subvolume set-default 256 /mnt/data/mysql

4.3 Btrfs 透明压缩#

# 启用压缩挂载
mount -o compress=zstd:3 /dev/sda1 /mnt/data
# 压缩级别:1(最快)到 15(最高压缩比),推荐 3
# 查看压缩效果
compsize /mnt/data/mysql
# Type Perc Disk Usage Uncompressed Referenced
# TOTAL 45% 2.3G 5.1G 5.1G
# 对数据库的影响:
# - 减少磁盘空间占用
# - 减少实际 I/O 量(CPU 换 I/O)
# - 可能增加写入延迟(压缩开销)

4.4 Btrfs 的优缺点#

优点缺点
快照功能强大CoW 导致碎片化严重
透明压缩减少空间随机写性能不如 ext4/XFS
内置 RAIDRAID 5/6 仍有稳定性问题
数据校验防静默损坏不适合重度随机写场景
子卷管理灵活修复工具不如 ext4 成熟
Note

Btrfs 的 CoW 机制与数据库的写入模式存在冲突。数据库(尤其是 InnoDB)频繁原地更新数据页,而 Btrfs 每次更新都创建新副本,导致严重的碎片化和写放大。建议数据库使用 ext4 或 XFS,Btrfs 更适合备份、归档和容器存储场景。

五、文件系统与数据库性能#

5.1 文件系统选择指南#

场景推荐文件系统原因
MySQL/PostgreSQLext4 或 XFS成熟稳定,随机写性能好
数据仓库/大文件XFS大文件性能优秀,并行 I/O
容器存储Btrfs 或 OverlayFS子卷/快照/层叠
备份归档Btrfs快照、压缩、校验
临时数据ext4 (nojournal)最快,无需持久性

5.2 数据库文件系统调优#

/etc/fstab
# MySQL 推荐配置
/dev/nvme0n1p1 /var/lib/mysql ext4 defaults,noatime,nodiratime,data=ordered 0 0
# PostgreSQL 推荐配置
# WAL 目录单独挂载(如果可能)
/dev/nvme0n1p2 /var/lib/postgresql/16/main/pg_wal xfs noatime,nodiratime 0 0
# 数据目录
/dev/nvme0n1p3 /var/lib/postgresql/16/main xfs noatime,nodiratime 0 0
# 通用优化
# 增大文件描述符限制
echo "* soft nofile 65535" >> /etc/security/limits.conf
echo "* hard nofile 65535" >> /etc/security/limits.conf
# 禁用 transparent huge pages(数据库推荐)
echo never > /sys/kernel/mm/transparent_hugepage/enabled

5.3 文件系统对写入性能的影响#

# 文件系统写入开销对比
import time
filesystems = {
"ext4 (journal)": {"overhead": "2x", "safety": "高"},
"ext4 (ordered)": {"overhead": "1.3x", "safety": "高"},
"ext4 (writeback)": {"overhead": "1.1x", "safety": "低"},
"XFS": {"overhead": "1.2x", "safety": "高"},
"Btrfs (zstd)": {"overhead": "1.5x", "safety": "高"},
"ext4 (nojournal)": {"overhead": "1.0x", "safety": "低"},
}
for fs, info in filesystems.items():
print(f"{fs:25s} | 写入开销: {info['overhead']:5s} | 安全性: {info['safety']}")

六、文件系统崩溃一致性#

6.1 崩溃一致性问题#

文件系统在写入过程中断电,可能导致以下不一致:

不一致类型说明后果
元数据不一致inode 指向已释放的块数据丢失或损坏
数据-元数据不一致数据未写入但元数据已更新读到旧数据或零
日志不一致日志记录不完整恢复失败
空闲空间不一致空闲位图与实际不符空间泄漏或重复分配

6.2 日志如何保证一致性#

sequenceDiagram participant App as 应用 participant JBD as JBD2 日志 participant FS as 文件系统 participant Disk as 磁盘 App->>JBD: 1. 开始事务 T1 JBD->>Disk: 2. 写入日志头(T1 开始) JBD->>Disk: 3. 写入日志记录(元数据变更) JBD->>Disk: 4. 写入日志尾(T1 提交) Note over JBD,Disk: 此时 T1 已提交,即使崩溃也能恢复 JBD->>FS: 5. Checkpoint:将变更写入实际位置 FS->>Disk: 6. 更新 inode FS->>Disk: 7. 更新数据块 FS->>Disk: 8. 更新位图 Note over FS,Disk: Checkpoint 完成后,日志空间可回收

6.3 fsync 与写屏障#

// 数据库如何保证数据持久化
void db_flush_page(int fd, void *page, size_t size) {
// 1. 写入 WAL(必须先于数据页落盘)
write(wal_fd, wal_record, wal_size);
fdatasync(wal_fd); // 确保 WAL 先落盘
// 2. 写入数据页
write(fd, page, size);
fdatasync(fd); // 确保数据页落盘
// fdatasync vs fsync:
// fdatasync — 只刷新数据,不刷新元数据(更快)
// fsync — 刷新数据 + 元数据
}
// 写屏障(Write Barrier)
// 文件系统通过写屏障保证写入顺序
// barrier=1 时,文件系统在关键点发送 CACHE FLUSH 命令
// 确保 WAL 写入在数据写入之前完成

七、实战:文件系统观察与调优#

7.1 文件系统信息查看#

# 查看文件系统类型和挂载选项
findmnt -n -o TARGET,FSTYPE,OPTIONS
# ext4 详细信息
dumpe2fs /dev/sda1 | head -50
# XFS 详细信息
xfs_info /mnt/data
# Btrfs 详细信息
btrfs filesystem df /mnt/data
btrfs device usage /mnt/data

7.2 文件系统碎片化分析#

# ext4 碎片化检查
e4defrag -c /var/lib/mysql/ibdata1
# XFS 碎片化检查
xfs_db -c "frag -f" /dev/sda1
# Btrfs 碎片化检查
btrfs filesystem df /mnt/data

7.3 文件系统基准测试#

# 使用 fio 测试不同文件系统的性能
# ext4
mount -t ext4 -o noatime,data=ordered /dev/sda1 /mnt/ext4
fio --name=ext4-test --directory=/mnt/ext4 --rw=randwrite --bs=4k \
--size=1G --numjobs=4 --iodepth=32 --ioengine=libaio --direct=1
# XFS
mount -t xfs -o noatime /dev/sdb1 /mnt/xfs
fio --name=xfs-test --directory=/mnt/xfs --rw=randwrite --bs=4k \
--size=1G --numjobs=4 --iodepth=32 --ioengine=libaio --direct=1
# Btrfs
mount -t btrfs -o noatime,compress=zstd:3 /dev/sdc1 /mnt/btrfs
fio --name=btrfs-test --directory=/mnt/btrfs --rw=randwrite --bs=4k \
--size=1G --numjobs=4 --iodepth=32 --ioengine=libaio --direct=1
Tip

选择文件系统时,优先考虑你的工作负载模式。大量小文件选 XFS(动态分配 inode),大文件顺序写选 ext4(延迟分配优化),需要 CoW 快照选 Btrfs。没有”最好的文件系统”——只有”最适合的文件系统”。

八、总结#

主题核心要点关键词
ext4最成熟稳定,日志模式灵活,适合数据库通用场景日志模式, 稳定性
XFS大文件性能优秀,并行 I/O 能力强,适合数据仓库并行 I/O, 大文件
BtrfsCoW 机制带来快照和压缩,但随机写性能较差,适合备份和容器CoW, 快照
崩溃一致性日志机制保证断电后数据完整,fsync 和写屏障是关键fsync, 写屏障
数据库选型MySQL/PostgreSQL 推荐 ext4 或 XFS,避免 Btrfsext4, XFS

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

文件系统
https://blog.souloss.com/posts/storage/storage-filesystem/
作者
Souloss
发布于
2025-05-01
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时