SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-30

ZFS 在 FreeBSD 與 Linux 上要多少 RAM?

ZFS 提供校驗和、快照、send/receive 複寫與壓縮,但 ARC 會占用 RAM。本指南說明如何在 2 GB 或 4 GB VPS 評估這項代價。

ZFS 提供的功能,以及它的代價

FreeBSD 與 Linux 上的 ZFS 現在共用同一套程式碼,也就是 OpenZFS,因此兩個系統的功能相同。執行 ZFS 的伺服器具備校驗和資料、在資料變更前不占用額外空間的快照、使用 zfs send 進行複寫,以及只需設定一個屬性即可啟用的壓縮功能。代價是記憶體:ARC(adaptive replacement cache)預設會占用大量 RAM;而在 2 GB 或 4 GB 的 VPS(virtual private server)上,這些記憶體正是應用程式所需的資源。

本指南是從租用的 VPS 來評估 ZFS。這類 VPS 通常只有一或兩個虛擬磁碟,不是配備四十個磁碟槽的儲存伺服器。從這種環境轉移後仍然實用的功能,才值得投入時間。無法適用於這種環境的部分,則應在建立儲存池前先了解。

FreeBSD 與 Linux 上的 OpenZFS:同一份程式碼,兩種套件提供方式

FreeBSD 自 2008 年的 FreeBSD 7.0 起,就將 ZFS 納入基礎系統,最初是實驗性功能。自 2020 年 12 月的 OpenZFS 2.0 起,FreeBSD 與 Linux 會從同一個原始碼樹建置,因此 zfs 和 zpool 在兩者上的行為相同,而且在其中一個系統建立的儲存池,也能在另一個系統匯入。

ZFS 在 Linux 上是套件,在 FreeBSD 上則屬於基礎系統,原因在於授權條款。OpenZFS 採用 CDDL(通用開發與散布授權條款)。Linux kernel 採用 GPL(通用公共授權條款)version 2。Linux kernel 專案認定兩者不相容,因此 ZFS 程式碼不會合併至 mainline Linux,而各個發行版則自行決定如何提供 ZFS。FreeBSD 沒有這項衝突,因此 ZFS 直接包含在系統中。實際上只有一項套件提供方式的差異,不需要對此採取任何立場。

SSD Nodes 不提供 FreeBSD 映像檔,因此在此租用的伺服器上,適用本指南的 Linux 部分。如果你在其他地方執行 FreeBSD,FreeBSD 伺服器會直接提供 ZFS,不需要建置模組,也不必因應 kernel 升級。

安裝 ZFS 並建立儲存池

在 Ubuntu 中,模組已包含於核心套件,因此只需安裝命令列工具。

sudo apt update
sudo apt install -y zfsutils-linux
zfs version

zfs version 會輸出兩行,分別是使用者空間版本與核心模組版本。若只輸出一行,表示模組未載入。套件位於 universe 元件中。Ubuntu 伺服器映像預設會啟用此元件;若 apt 找不到套件,請先執行 sudo add-apt-repository universe。

在 Debian 中,套件位於 contrib 元件中,模組則由 DKMS(dynamic kernel module support)在本機建置。將 contrib 加入 /etc/apt/sources.list.d/debian.sources 中的 Components: 行,執行 sudo apt update,然後執行:

sudo apt install -y linux-headers-$(dpkg --print-architecture) zfs-dkms zfsutils-linux

安裝程序會編譯模組,並輸出 Building initial module for 6.12.0-...。這需要幾分鐘。請注意其含義:每次核心升級都會重新建置模組;如果建置失敗,儲存池會保持未匯入狀態,直到你修正問題。

在 FreeBSD 中,不會自動安裝任何項目。請啟用服務並啟動服務。

sysrc zfs_enable=YES
service zfs start

接著建立儲存池。先查看穩定的裝置路徑,因為 /dev/vdb 會依偵測順序分配,新增另一個磁碟區後可能改變。

ls -l /dev/disk/by-id/
sudo zpool create -o ashift=12 tank /dev/disk/by-id/virtio-abc123def456
zpool status tank

zpool status 應輸出 state: ONLINE,並在 tank 下列出你的裝置。ashift=12 會將儲存池的最小區塊固定為 4 KiB,這符合目前的 SSD,而且建立後無法變更。

多數租用的映像會從 ext4 根檔案系統開機,因此這裡的 ZFS 是位於第二個磁碟區上的資料儲存池,而不是根檔案系統。建立儲存池前,請確認裝置就是你預期的裝置,因為確認你購買的 NVMe 磁碟只需一分鐘,而重建需要一個下午。

僅在儲存池具備備援時,checksum 才能修復資料

ZFS 寫入的每個區塊都會帶有 checksum,每次讀取時也都會驗證。偵測永遠可正常運作。修復則需要第二份副本。

在單磁碟儲存池上,ZFS 會如實回報問題,然後停止處理。zpool status -v 會以如下方式回報:

status: One or more devices has experienced an error resulting in data
        corruption.
action: Restore the file in question if possible.  Otherwise restore the
        entire pool from backup.
errors: Permanent errors have been detected in the following files:

        /tank/data/archive.tar

系統會指出損壞的檔案名稱。ext4 會直接回傳那些位元組而不提供任何提示,因此這項功能本身就很有價值。但 ZFS 仍無法修復檔案,因為儲存池中沒有第二份副本可供修復。

使用 mirror 時,同一項讀取會從正常的一側提供資料,損壞的區塊會被重新寫入,事件則會出現在 zpool status 的 CKSUM 欄位中。這就是自我修復,但需要兩個裝置。

sudo zpool create -o ashift=12 tank mirror /dev/disk/by-id/DISK1 /dev/disk/by-id/DISK2

在 VPS 上,主機的儲存設備通常已具備備援,常見情況是 虛擬機器管理程式下的 RAID 10。這能防止磁碟故障,但無法告訴你某個區塊回傳的資料是否錯誤,因為陣列無法判斷哪一份副本才是正確資料。ZFS 可以判斷,因為它會將資料與自身寫入的 checksum 比對。

如果你只有一個虛擬磁碟,但希望具備部分修復能力,sudo zfs set copies=2 tank/important 會在同一個磁碟上,為該 dataset 的每個區塊儲存兩份副本。這會使該 dataset 使用的空間加倍,可在區塊損壞時存活,但在整個 volume 消失時無法發揮作用。

scrub 會讀取儲存池中的所有資料並進行驗證。

sudo zpool scrub tank
zpool status tank

狀態正常的儲存池最後會顯示類似 scan: scrub repaired 0B in 00:04:11 with 0 errors 的一行。請排程定期執行;小型儲存池每月一次即可。

systemctl list-unit-files 'zfs-scrub*'
sudo systemctl enable --now zfs-scrub-monthly@tank.timer

Dataset 是套用原則的單位

Dataset 是 pool 內的檔案系統,而且建立成本低,因此每個工作建立一個 Dataset。屬性會從 pool 向下繼承,表示只需設定一次預設值,再針對需要的地方覆寫即可。在 FreeBSD 上,jail 通常就是這樣執行:每個 jail 使用一個 Dataset,因此可以個別建立 snapshot 並回復,這也是 jail 與 Docker container 的差異之一。

sudo zfs create tank/data
sudo zfs create tank/pg
sudo zfs set compression=lz4 tank
sudo zfs set atime=off tank
sudo zfs set quota=20G tank/data
sudo zfs set recordsize=16K tank/pg
zfs get -r compression,compressratio,quota tank

Compression 是許多人因為謹慎而不啟用的屬性,但這種做法並不正確。lz4 只會增加少量 CPU 負載,並減少必須寫入磁碟的資料量,因此對可壓縮資料而言,通常能加快讀寫速度。zstd 會使用更多 CPU 進行更高程度的壓縮,適合很少讀取的 log 與 archive。使用 zfs get compressratio tank 檢查實際效果,並記住壓縮比例只會計入設定該屬性後寫入的資料。

recordsize 是 Dataset 寫入的最大 block 大小,預設為 128K。資料庫將 8 KiB page 寫入 128 KiB record 時,一次小型寫入會變成讀取整個 record、修改後再寫回。請在載入資料前,於資料庫 Dataset 上設定 recordsize=16K,因為此屬性只會套用到新寫入的 block。

quota 可避免單一 Dataset 填滿 pool。ZFS pool 接近 100% 使用率時,效能會降低,清理也會變得困難,因此應刻意保留可用空間。

Snapshot 在資料變更前不佔用額外空間

ZFS 絕不會覆寫正在使用的區塊。它會寫入新的區塊並更新指標,這就是 copy-on-write 的含義。Snapshot 是一項記錄,表示「保留此資料集目前指向的區塊」,因此建立 snapshot 會立即完成,且不會產生額外空間成本。

sudo zfs snapshot tank/data@2026-08-11
zfs list -t snapshot -o name,used,refer -r tank/data

Snapshot 的 USED 欄位表示僅由該 snapshot 保留的空間。起初接近零,隨著您修改或刪除資料而增加,因為舊區塊已無法釋放。

取回檔案不需要執行還原步驟。

ls /tank/data/.zfs/snapshot/
cp /tank/data/.zfs/snapshot/2026-08-11/notes.txt /tank/data/notes.txt

在執行 sudo zfs set snapdir=visible tank/data 前,連 ls -a 都看不到 .zfs 目錄。請在需要之前先建立 snapshot,因為沒有 snapshot 時,一次誤執行 rm -rf 就會讓您進入 ext4 復原流程;該流程會先卸載磁碟,後續處理只會更加複雜。

Rollback 會捨棄 snapshot 之後寫入的所有內容。

sudo zfs rollback tank/data@2026-08-11

如果存在較新的 snapshot,rollback 會拒絕執行;-r 會刪除這些較新的 snapshot,讓 rollback 繼續執行。按下 Enter 前,請再次確認資料集名稱。

Snapshot 不是備份。 Snapshot 位於同一個 pool、同一個 volume 及同一台伺服器上。Volume 故障或任何 zpool destroy 都會讓 snapshot 與資料一併遺失。Snapshot 能防範您自己的 rm 以及錯誤升級,這涵蓋許多實際事故;但對 pool 本身發生的任何問題都無法提供保護。完整說明請參閱:為什麼 VPS snapshot 不是備份。

傳送與接收:以一個命令完成複寫

zfs send 會將快照轉換為標準輸出上的位元組串流,而 zfs receive 會將該串流還原為資料集。第一次複製會進行完整傳送。

sudo zfs snapshot tank/data@daily-2026-08-11
sudo zfs send tank/data@daily-2026-08-11 | ssh backup.example.com "sudo zfs recv -F backup/data"

之後只傳送兩個快照之間的變更內容。

sudo zfs snapshot tank/data@daily-2026-08-12
sudo zfs send -i tank/data@daily-2026-08-11 tank/data@daily-2026-08-12 | ssh backup.example.com "sudo zfs recv backup/data"

接收端仍必須保有您要傳送的來源快照。若接收端沒有該快照,接收會因 cannot receive incremental stream: most recent snapshot of backup/data does not match incremental source 而停止,因為 ZFS 沒有可套用差異的基準。請從兩端都持有的快照開始傳送,或重新進行完整傳送。

請在目標端授予權限,不要使用遠端 root:sudo zfs allow -u backupuser create,mount,receive backup/data。

這是真正的異地備份,但有一項條件。遠端端點必須是 ZFS pool,因為 object storage 無法接收串流。若目標是 S3-compatible storage 或一般 Linux 主機,請使用可與其通訊的工具;從 VPS 執行 restic 備份涵蓋這種情境。

為什麼 ZFS 會使用這麼多 RAM?ARC

ARC(adaptive replacement cache)是 ZFS 的讀取快取。它位於 kernel memory,而不是一般的 Linux page cache,因此 free -h 不會在 buff/cache 下回報它。它會被計入使用中的記憶體。看起來記憶體幾乎用滿的 ZFS 主機,通常只是快取已經累積內容;多數「ZFS 吃掉我的 RAM」的回報都是這個原因。

預設上限刻意設定得很寬鬆。OpenZFS 2.3 會將 ARC 大小上限設為 RAM 減去 1 GiB 與 RAM 的 5/8 兩者中較大者。OpenZFS 2.2 及更早版本在 Linux 上使用 RAM 的一半,而 FreeBSD 早已採用較新的規則。執行 zfs version,即可確認目前適用的規則。

ChartDefault maximum ARC size by instance RAM, from the OpenZFS default rule (GiB)
The data behind this chart
[
  {
    "label": "2 GB VPS",
    "openzfs_2_2_linux_gib": 1,
    "openzfs_2_3_gib": 1.25
  },
  {
    "label": "4 GB VPS",
    "openzfs_2_2_linux_gib": 2,
    "openzfs_2_3_gib": 3
  },
  {
    "label": "8 GB VPS",
    "openzfs_2_2_linux_gib": 4,
    "openzfs_2_3_gib": 7
  },
  {
    "label": "16 GB VPS",
    "openzfs_2_2_linux_gib": 8,
    "openzfs_2_3_gib": 15
  }
]

這些數值是將文件記載的預設規則套用至常見 instance 大小後得到的結果,不是從執行中的主機測得的數值。在 4 GB instance 上,2.3 規則允許 ARC 使用 3 GiB。同一台主機若使用 2.2,則上限為 2 GiB。在 2 GB instance 上,2.3 規則仍允許使用 1.25 GiB。應用程式只能使用剩餘的記憶體。

請直接從自己的伺服器讀取實際數值,不要只依賴表格:

grep -E '^(size|c_max) ' /proc/spl/kstat/zfs/arcstats
arc_summary | head -n 20

第三欄的單位是 bytes。c_max 是目前生效的上限,size 是 ARC 目前持有的大小。

ARC 確實會釋放記憶體。kernel 偵測到記憶體壓力後,ARC 會縮小。問題在於時機,因為縮減是由記憶體壓力觸發的;因此,程序一次要求數百 MiB 記憶體時,可能會在 ARC 尚未完成釋放前觸發 OOM(out of memory)killer。在執行資料庫與網頁伺服器的 2 GB 主機上,這並不是罕見情況。OpenZFS 手冊對手動變更也有相同說明:降低上限「不會在沒有記憶體壓力促使 ARC 縮減的情況下,讓 ARC 自行縮小」。

如何在小型 VPS 上限制 ARC

先決定工作負載需要多少記憶體。加總資料庫與應用程式的需求,為作業系統保留餘裕,再將剩餘記憶體分配給 ARC。在執行 Postgres 和一個 Web 應用程式的 4 GB 執行個體上,512 MiB 到 1 GiB 的 ARC 是合理的起始值。

以位元組為單位即時設定。以下設定為 1 GiB。

echo 1073741824 | sudo tee /sys/module/zfs/parameters/zfs_arc_max

讓設定在重新開機後仍然生效。

echo 'options zfs zfs_arc_max=1073741824' | sudo tee /etc/modprobe.d/zfs.conf
sudo update-initramfs -u

initramfs 步驟很重要,因為模組可能會在掛載根檔案系統前從 initramfs 載入。這表示模組不會讀取你剛才寫入的檔案。重新開機後,使用 arcstats 中的 c_max 行確認設定。

手冊本身指出兩項注意事項。系統執行期間無法將值設回 0,因此若要復原,必須編輯檔案並重新開機。此外,降低數值不會立即縮小已經很大的 ARC。

在 FreeBSD 上,相同的限制位於 vfs.zfs.arc 下的 sysctl。執行 sysctl vfs.zfs.arc 查看目前的值,以及該版本使用的確切名稱,然後將最大值寫入 /boot/loader.conf。

小型伺服器還有兩項記憶體規則。請關閉 deduplication,因為 dedup table 會佔用記憶體;常見的經驗法則是每 1 TB 唯一資料需要 1 到 3 GB RAM。此外,不要將 swap 放在 zvol 上。zvol 是從 pool 中切出的區塊裝置。透過正嘗試釋放記憶體的檔案系統進行交換,可能導致機器死結。請將 swap 放在 pool 外部的純分割區或 swap file 上。

何時 ext4 或 XFS 搭配 restic 才是較佳選擇

伺服器具備多餘記憶體與第二個磁碟區時,ZFS 才能發揮效益。除此之外,使用一般檔案系統搭配可靠的備份工具通常更合適。符合以下情況時,請選擇 ext4 或 XFS:

  • 執行個體只有 2 GB 或 4 GB RAM,而工作負載需要使用全部記憶體。
  • 只有一個虛擬磁碟,沒有第二份副本,因此 ZFS 只能偵測問題,無法進行修復。
  • 備份目標是物件儲存或一般 Linux 主機,對方無法接收 zfs send 串流。
  • 使用 Debian 搭配 DKMS,無法承受核心升級後模組未成功建置。
  • 需要在根檔案系統上使用 ZFS,但供應商提供的映像檔只有 ext4。

如果具備獨立的資料磁碟區、充足的 RAM(8 GB 以上較為寬裕),並且有實際使用快照與 zfs send 的規劃,而不只是啟用這些功能,請保留 ZFS。其他情況下,使用 ext4 搭配 restic,將加密且去重的備份寫入伺服器無法控制的儲存空間,就能涵蓋大多數相同需求,而且不會佔用額外記憶體。

故障情況與對應訊息

重新開機後 pool 不見了。 zpool status 會輸出 no pools available。import service 會讀取 /etc/zfs/zpool.cache,因此未列在該檔案中的 pool 不會在開機時匯入。sudo zpool import 會列出可匯入的 pool,sudo zpool import tank 會將其匯入,sudo zpool set cachefile=/etc/zfs/zpool.cache tank 會讓設定持續生效。若 pool 未從其他系統正常匯出,會顯示 cannot import 'tank': pool may be in use from other system;確認沒有其他主機持有該 pool 後,可使用 sudo zpool import -f tank 覆寫這項限制。

Debian 在 kernel 升級後出現 modprobe: FATAL: Module zfs not found in directory /lib/modules/6.12.0-...。 DKMS 沒有針對新 kernel 建置模組,通常是因為未安裝相符的 headers。dkms status 會顯示各 kernel 已建置的模組。接著 sudo apt install -y linux-headers-$(uname -r) 會執行 sudo dkms autoinstall 重新建置,sudo zpool import tank 則會讓 pool 恢復可用。

pool 已滿,但你已刪除檔案。 只要 snapshot 仍參照已刪除的資料,這些資料就會繼續佔用磁碟空間,因此 du 和 df 的結果會不一致。zfs list -o space -r tank 會將使用量拆分為 USEDDS 和 USEDSNAP;USEDSNAP 數值很大即表示原因在此。使用 sudo zfs destroy tank/data@2026-06-01 destroy 舊的 snapshot 後,空間就會釋出。

zpool status 中的 CKSUM 計數持續增加。 ZFS 下方的某個元件回傳了錯誤資料。在 mirror 上,這是警告,且資料區塊已修復。在單磁碟 pool 上,檔案會遺失;zpool status -v 會指出檔案名稱。請從不在此 pool 中的備份還原該檔案。

伺服器速度變慢且開始使用 swap。 先依上述方式限制 ARC 大小,再執行 arc_summary 並查看命中率。若 ARC 太小,無法容納工作集,每次讀取都必須存取磁碟。此時,使用 page cache 的一般檔案系統反而會更適合。

FAQ

ZFS 在 VPS 上需要多少 RAM?

ZFS 可在 2 GB 的執行個體上運作。真正的問題是應用程式還能使用多少記憶體。未調整設定時,OpenZFS 2.3 會讓 ARC 擴張至 RAM 減去 1 GiB 與 RAM 的 5/8 兩者中較大的值,因此 4 GB 的主機可將 3 GiB 提供給快取。將 zfs_arc_max 設為工作負載可分配的數值,然後從 /proc/spl/kstat/zfs/arcstats 讀取 c_max 行進行確認。

ZFS 快照是備份嗎?

不是。快照與資料位於同一個 pool。它能在發生 rm 或升級失敗時保留,但 pool 或執行個體遺失時也會一併遺失。使用 zfs send 將快照傳送至另一台機器,或執行會將資料寫入本伺服器無法控制之儲存設備的備份工具,才能將快照轉成備份。

ZFS 在 FreeBSD 與 Linux 上的運作方式相同嗎?

自 OpenZFS 2.0 於 December 2020 發布後,兩者使用相同的程式碼基礎、相同的命令與相同的磁碟格式,pool 也能在兩者之間移動。差異在於封裝方式。FreeBSD 將 ZFS 隨附於基礎系統。Linux 則由各發行版自行決定:Ubuntu 將模組建置在其 kernel 套件中,而 Debian 使用 DKMS 在你的機器上建置模組,因此 kernel 升級後,可能會在重新建置成功前暫時沒有模組可用。

ZFS 能在只有一顆磁碟的 VPS 上修復損毀嗎?

它能偵測損毀並指出檔案,但無法修復,因為修復需要區塊的第二份副本。在 dataset 上設定 zfs set copies=2,即可用兩倍的空間保留第二份副本。這能處理損毀的區塊,但無法處理遺失的 volume。跨兩個 volume 建立 mirror,才是真正能自動修復的方案。

壓縮會讓伺服器變慢嗎?

lz4 通常會讓伺服器更快。壓縮區塊代表需要寫入與讀取的位元組更少,而每個區塊的 CPU 成本相較於節省的磁碟 I/O 很小。將 compression=lz4 設定在 pool 根目錄,讓所有 dataset 繼承此設定,然後在實際寫入資料後檢查一次 zfs get compressratio tank。