檔案系統與儲存

寫入時複製檔案系統(copy-on-write filesystem)

/ ZFS = zee-eff-ess; Btrfs = BUT-ter-eff-ess /

大多數較舊的檔案系統靠就地覆寫區塊來更新檔案:要改一個區塊,你在同一個磁碟位置把新位元組寫在舊的之上。那有效率但危險——若覆寫途中斷電,你可能落得既無舊區塊、也無完整新區塊的下場,一個撕裂的區塊。寫入時複製檔案系統根本拒絕覆寫活資料。要改任何東西,它把新版本寫到一個全新的、未使用的位置,然後才翻動一個指標使新版本正式生效。舊資料在不再被參考之前一直不被碰。

走一遍在 ZFS 或 Btrfs 這類 CoW 檔案系統裡編輯檔案一個區塊的過程。資料是透過一棵從根開始的指標樹可達的。要修改一個葉區塊,檔案系統 (1) 把改過的區塊寫到一個新的空閒位置、(2) 寫一份指向新葉的父指標區塊的新副本,依此一路往上到一個新的根——這叫做沿樹往上寫。(3) 最後它原子地把那唯一的超級區塊指標更新到新根。在那最後一翻之前,整棵舊樹仍然有效且一致;之後,新樹才是。任何層級都沒有就地編輯,所以當機只是把舊的一致樹原封不動留下。這些檔案系統也對每個區塊做檢查碼、並把檢查碼存在父指標裡,於是損壞(翻轉的位元、誤導的寫入)在讀取時被偵測到,並在有備援時從一份好副本自我修復。

為何重要:寫入時複製幾乎免費地給出當機一致性(在新狀態完成之前舊狀態永不被摧毀),並使快照近乎瞬時——一個快照只是把舊的根指標留著、共享所有未變的區塊。誠實的取捨:寫入會放大(改一個區塊可能弄髒一條一路到根的父區塊鏈)、空閒空間可能碎片化(因為新版本落在哪裡有空就在哪裡)、而且推理空閒空間更難(因為只要還有任何快照參考某區塊,它就不能被重用)。

在 CoW 樹裡編輯一個葉區塊: 舊: root -> ... -> parent -> leaf(v1) 把 leaf(v2) 寫到一個新區塊、寫 parent' -> leaf(v2)、... 新的 root' 原子地:超級區塊指標 = root' <- 舊樹在這一翻之前一直有效

絕不就地覆寫:把新版本寫到別處、再原子地翻動根指標。

寫入時複製用寫入放大與更難的空閒空間記帳,換取當機安全與便宜的快照。一個常見的迷思是 CoW 讓 fsync() 變得不必要——它沒有;你仍必須沖刷到磁碟才能確知你的資料持久。CoW 保證的是磁碟上的樹永遠一致,而非你未沖刷的寫入抵達了它。

又稱
CoW filesystemnever-overwrite-in-placeZFSBtrfs寫時複製檔案系統