檔案系統與儲存

當機一致性(crash consistency)

電源可能斷、核心可能 panic、機器可能在任何一瞬被拔掉——包括正在寫磁碟的途中。當機一致性問的是:在這樣一次中斷之後重新開機時,你的資料與檔案系統處於什麼狀態。令人不安的真相是:儲存堆疊「不」承諾你最後的寫入存活下來;它做的是窄得多的保證,而一個想讓自己的資料可復原的應用,必須恰恰建立在那些窄保證之上、不多不少。

兩個硬現實框住了這件事。第一,在應用層級,寫入既非瞬時也非全有或全無:一次大的 write() 可能被拆成數個區塊寫入,當機可能落下一些、不落下另一些——而即使是單一個區塊也可能被撕裂(一半舊、一半新),若裝置的原子寫入單元小於你的區塊、或寫入正在途中時斷電。第二,寫入可能被重新排序:頁面快取、區塊層、以及磁碟機自己的快取,可能以與你發出時不同的順序把你的寫入提交到碟片,所以你不能假設「因為寫入 B 在寫入 A 之後完成,B 就在 A 之後抵達磁碟」。你真正能依賴的唯一順序,是你用一個持久性屏障——fsync()——所強制的順序,或是一個日誌或寫入時複製檔案系統為它自己的後設資料所提供的順序。其餘一切都是希望,而非保證。

為何重要:真正當機安全的格式(資料庫、訊息日誌、謹慎的設定寫手)正是由這些原語建成的——寫資料、對它 fsync、再寫一個小小的提交標記、再 fsync 一次,使當機要麼留著標記(資料完整)、要麼沒留(忽略不完整的資料)。誠實而常令人意外之點:「檔案系統有日誌、所以我安全」保護的是檔案系統自己的一致性,而非你應用跨多個檔案或紀錄的不變式。如果你的程式需要 A 與 B 原子地一起改變,你必須自己用順序與 fsync 強制它;儲存堆疊不會替你做,而假設它會,是當機後資料損壞的一大主因。

當機安全的提交模式: write(data); fsync(fd); // 第 1 步:資料落穩定儲存 write(commit_marker); fsync(fd); // 第 2 步:標記在資料「之後」 // 第 2 步前當機 -> 無標記 -> 忽略不完整的資料 // 第 2 步後當機 -> 標記在 -> 已知資料完整

靠 fsync 排序,把「也許寫了」變成乾淨的全有或全無:標記存在當且僅當資料完整。

日誌檔案系統保持的是「它自己的」後設資料在當機後一致;它不保持你應用跨檔案或跨紀錄的不變式。寫入也可能被重新排序、個別區塊可能被撕裂,所以你能依賴的唯一「寫入間順序」是你用 fsync 強制的那個。假設儲存堆疊會原子地提交你的多步更新,是當機後損壞的一大主因。

又稱
power-fail consistencyordering guaranteestorn writes當機一致性斷電一致性