nixos系统的硬盘规划
现状(2026-10-08 实测)
| 盘 | 型号 | 容量 | 分区 |
|---|---|---|---|
| nvme0n1 | BC711 NVMe SK hynix | 119.2 GiB | p1 1 GiB vfat /boot(用 161 MiB)· p2 118.2 GiB ext4 /(用 35 G,剩 76 G) |
| nvme1n1 | CWESR02TBTLCZ-27J-2 | 1.86 TiB | p1 551 MiB vfat BWINEFI · p2 295.1 GiB ntfs s500win · 未分配约 1.57 TiB |
目标布局:盘1 入主
盘1 nvme1n1 1.86 TiB —— 主力盘
p1 551 MiB vfat BWINEFI Windows ESP,原样保留
p2 295.1 GiB ntfs s500win 后备 Win,原样保留(零成本,将来可回收)
p3 1 GiB vfat NIXESP 新建:NixOS 自己的 ESP(与 Windows 分开)
p4 256 GiB ext4 / NixOS root
p5 ~1.32 TiB 待定 /s500data 挂载点(文件系统待定)
盘2 nvme0n1 119.2 GiB —— 原样不动 = 一台完整可启动的备用 NixOS
/s500data 下平铺(同一层,不嵌套):
vms/ repos/ docker/ <个人数据目录…>分盘判据
| 决策 | 理由 |
|---|---|
| root 倾向 ext4(不倾向 ZFS) | NixOS generation 本身就是 root 的时点回退(启动菜单切代),ZFS root 增量价值小;ext4 保住「任何 U 盘都能 mount 修」 |
| 挂载点命名用业务 / 设备名 | 不用 dataset / pool / tank 这类技术概念名 —— 名字不得预设技术选型,否则读方案的人会默认朝某个技术走 |
| /s500data 的文件系统不预设 | 判据:要快照 / 校验 / zfs send 到冷备 → ZFS;要任何 U 盘可 mount、可 fsck → ext4。技术选择留到建盘前定 |
| root 给 256 GiB(比现在大一倍) | ZFS 只能长不能缩,所以 root 给宽是安全的;root 增长源只有 /nix/store,配自动 gc 后稳定 |
| ESP 与 Windows 分开 | Windows 功能更新会重写引导项;分开后 NixOS 的 generation 回退链不受盘1 任何操作波及 |
| 盘2 原样保留 | 零成本救援系统,也是未来唯一扩容位(内部仅 2 个 M.2 位,已占满) |
| 不建任何冗余 | 内部没有第三盘位,mirror / raidz 物理上无从谈起 |
配套三件(缺一不可)
nix.gc.automatic = true; nix.gc.options = "--delete-older-than 14d";
nix.settings.auto-optimise-store = true; # 不给 gc,/nix/store 无界增长,root 迟早撑爆
boot.loader.systemd-boot.configurationLimit = 10; # ESP 写满 = rebuild 直接失败
boot.extraModprobeConfig = "options zfs zfs_arc_max=17179869184"; # 16 GiB(默认吃 50% = 32 G)不可逆点(建盘前必须一次定对;标 ZFS|的四项仅在选 ZFS 时适用)
| 项 | 当前选择 | 定错能改吗 |
|---|---|---|
| ZFS|加密 | 开 / 不开 | 不可逆:2.4 原生加密必须建 dataset 时开,明文不能转 |
| ZFS|ashift | 12(4K,NVMe 必须) | 建池定死 |
| ZFS|pool 名 + dataset 划分 | 见布局 | 拆分容易,合并要 send/receive 重写 |
| ZFS|recordsize(每 dataset) | 按用途分(VM 大块 / DB 小块) | 只对之后新写入的文件生效 |
| root 与数据盘的容量切分 | 256 GiB / 1.32 TiB | 事后不便宜 |
待定
- p4 root 用 ext4 还是 ZFS(判断倾向 ext4)
- p5 /s500data 用 ext4 还是 ZFS(判断倾向 ZFS:快照 / zfs send / 校验正是数据层缺的)
- 若选 ZFS:加密开不开(必须建 dataset 时定,事后不可逆)
- 盘1 的 295 GiB s500win 分区保不保(倾向保留)
- 待实测:盘1 那块 2 TB 的实际读写性能(决定「数据放这块盘」是升级还是降级)
ntfs,ext4,zfs,raid的分析
四种的定位
| 名称 | 定位 | 快照 | 校验 | 缩容 | 跨机可修 |
|---|---|---|---|---|---|
| ntfs | Windows 文件系统,日志 + ACL + 压缩 + 卷影副本 | 卷影副本 | 无端到端校验和 | 能 | Windows 原生,Linux 需 ntfs-3g |
| ext4 | Linux 通用日志文件系统 | 无 | 无 | 能(离线 resize2fs + fsck) | 任何 Linux 直接 mount |
| zfs | 存储池管理 + 文件系统 + 卷管理三合一 | 有,O(1) | 有,每块数据带校验和 | 只能长,永不能缩 | 需 ZFS 2.4+ 用户态 |
| raid | 把 N 块盘的地址空间映射成一个逻辑地址空间的存储虚拟化,本身不是文件系统 | 无 | 无(parity 只保证可读,不保证语义正确) | 不适用 | 不适用 |
抽象链对位(Windows 侧参照)
ZFS 的定位:云服务商的存储方案,不是个人电脑的物理存储管理。判据:它的能力清单(快照 / send-receive / quota / 池扩展 / 校验)就是云块存储的产品功能表;单盘机上只用到一小部分,却承担全部复杂度。Windows 侧的同位物是 Storage Spaces + ReFS,不是 diskpart —— diskpart 停在「分区」这一层。
| 名称 | 抽象链 | 它管什么 |
|---|---|---|
| diskpart | 物理盘 → 分区 → 卷 → 盘符 → NTFS | 只切盘子;不管完整性、快照、校验 |
| Storage Spaces + ReFS | 物理盘 → 池 → space(虚拟盘)→ 卷 → ReFS | 池化、mirror / parity / simple;ReFS 带校验和与 integrity stream |
| ZFS | 物理盘 → vdev → pool → dataset / zvol → 挂载点 | 上面两件 + 快照 + send/receive + 压缩 + quota + 校验 |
Storage Spaces / ReFS 细节未实测。
ZFS 一体两面:机制 · 收益 · 代价
先破一个宣称:单盘 ZFS 不提供 durability,和 ext4 一样会丢;增量只在时间维度(快照 / send)与可见性(校验)。
| 机制(ZFS 术语) | 收益面 | 代价 / 边界 |
|---|---|---|
| 冗余 vdev(mirror / raidz) | 单盘失效仍可读;raidz 用 parity 换容量效率 | 本机不适用 —— 只有一个数据盘位,单盘无冗余可言 |
| 校验和 + scrub(self-heal 的输入) | 每块数据带校验和;scrub 定期扫描可发现静默损坏 | self-heal 需要冗余;无冗余只能发现不能修。checksum error 的正确反应是换盘 + 从备份恢复 |
| COW 事务模型(copy-on-write / TXG) | 写入原子;快照与回退的地基 | 随机小写有写放大,数据库、容器 overlay 是弱区 |
| 快照(snapshot / hold) | O(1) 创建;时间点回退,普通文件系统没有的维度 | 不是备份 —— 与池同生共死,池毁快照全没;被快照引用的块延迟释放,持续占空间 |
| 复制原语(send / receive) | 增量、可校验、可自动化 —— 是「另一份数据在另一个介质上」的实现手段 | 依赖另置介质 + 定期任务;它只提供手段,保留多久要自己定策略 |
| 池化与配额(pool / quota / reservation) | 空间池内共享不切死;管理粒度细于分区 | 池只能扩不能缩 —— 划出去的空间拿不回来,一次定对;vdev 拓扑建池时定型 |
| 寻址与块参数(ashift / recordsize) | 可对齐 4K 扇区;recordsize 可按用途调(VM 大块 / DB 小块) | 建池、建 dataset 时定;recordsize 只对之后新写入的文件生效,全量改要 send/receive 重写 |
| 压缩(lz4 / zstd) | 1.5–2x 常见,CPU 代价低到可忽略 | 压缩率不是宣传值;已写入的数据改算法不追溯 |
| ARC 读缓存 | 用内存换读命中率,热点数据不落盘 | 默认上限 = 物理内存 50%(64 G 机上 32 G),与 VM 争抢,必须设 zfs_arc_max |
| 原生加密(AES-GCM / keylocation) | dataset 级加密,可用 passphrase 或 keyfile | 必须建 dataset 时开,明文不能转,不可逆;解锁发生在 initrd;密钥丢了数据也丢了 |
| 属性系统(properties) | 策略可下推到 dataset(quota / mountpoint / com.sun:auto-snapshot);NixOS 的声明式开关就是驱动它 | 属运行期状态,Nix 不描述它;「只对新写入生效」的那些属性改不动旧数据 |
| 交付形态(树外内核模块 zfs.ko + 用户态 CLI) | 独立于内核演进;控制面就是命令行 —— 无固件、无 option ROM、无独立管理界面 | 与内核版本强耦合(跑太快就构建失败或起不来;linuxPackages.zfs 属性名 2025-01 已被 nixpkgs 删除);救援门槛高一档 —— ext4 盘任何 Linux U 盘可 mount,ZFS 盘要 2.4+ 用户态 |
ZFS 的操作管理方式
ZFS 没有固件成分:无 BIOS 配置页、无 option ROM、无独立管理界面(TrueNAS / Cockpit 是第三方应用)。它是纯软件 —— 内核里 zfs.ko + 用户态 zpool / zfs 命令。
| 维度 | 硬件 RAID | ZFS |
|---|---|---|
| 存在形态 | 控制器固件里的 option ROM | 内核模块 zfs.ko + 用户态 CLI |
| 配置入口 | 开机 Ctrl+I / BIOS 里的配置页 | 系统跑起来之后的命令行 |
| 配置存放 | 控制器 NVRAM + 盘上 metadata | 池自身(写在盘的 label / uberblock 上) |
| 独立管理界面 | 有(固件阶段) | 无 |
| 换系统还认识吗 | 认识(固件层,与 OS 无关) | 需目标 OS 装了 ZFS 驱动 |
| 创建时机 | 装机 / 进 BIOS 时 | 系统运行中用命令行创建 |
| 使用时机 | 每次开机固件阶段就位 | initrd(root 在 ZFS 上时)或 systemd 阶段 |
关键区别:diskpart 是控制面工具,ZFS 是常驻数据通路。diskpart 改完分区表就退出;ZFS 的每次文件读写都真的走在 zfs.ko 里,zpool / zfs 只是它的控制面。它的全部代价来自这一点:吃 ARC、写放大、与内核版本耦合。
与 NixOS 的边界:Nix 管「哪个 dataset 挂到哪个路径」,不管池里长什么样。fileSystems."/s500data" = { device = "s500data"; fsType = "zfs"; }; 会被用来反推要 import 哪个池并生成 zfs-import-<池名>.service;挂载由 ZFS 自带的 systemd 单元做。池内形态(几个 dataset、recordsize、快照、属性)是运行期状态,用 zfs 命令改 —— 边界是「配置 vs 数据」。
| NixOS 声明式开关 | 作用 |
|---|---|
| boot.zfs.extraPools | 额外要在启动时 import 的池 |
| boot.zfs.requestEncryptionCredentials | initrd 中解锁加密 dataset |
| boot.zfs.forceImportRoot | 强制 import root 池(26.11 起默认 false) |
| services.zfs.autoSnapshot | 自动快照(frequent / hourly / daily / weekly / monthly 各自保留数) |
| services.zfs.autoScrub | 定期 scrub(默认 monthly,带随机延迟) |
| services.zfs.trim / services.zfs.zed | 定期 TRIM;ZFS 事件守护 |
结论
- ZFS 的增量价值不在冗余:它在普通文件系统之外多给了时间维度(快照 / send)与可见性维度(校验)。
- 单盘机上只应取这两样 —— 「防丢数据」买不到,也不该买。
- 有价值排序:send / 快照 >> 压缩 > 校验。
- 净影响:ZFS 在盘1 上就是「带快照与校验的文件系统」;真正的「不会丢」由 /s500data 定期 zfs send 到移动硬盘(离线、另一个物理介质、可校验)承载 —— 这正好就是备份层要建的东西。