我相信很多人都遇到過硬盤卡殼、掉鏈子的情況。當然,這篇文檔的主旨不是告訴你怎麼樣可以繞過那些老爺子寫的課本上說的金科玉律──重要的數據都應該有備份──如果你的數據最終丟失了,那麼我的問題是:你的備份呢?
但是,即使你有經常備份的習慣,有些數據還是會難免出現一些沒有及時備份而導致丟失的情況。我的觀點是,沒有備份計劃的數據都不是重要數據,不要等到數據丟失了再去後悔,但是我們顯然應該采取各種各樣的手段來阻止沒有及時備份的那一小部分數據的丟失。
硬盤
大家一起默念:它很便宜!它會壞掉!
是的,實戰經驗會告訴你,它很便宜!它也會壞掉!不管這個硬盤是來自什麼廠商,也不管它是SATA、SCSI、SAS或者是傳統的ATA接口,它出現故障只是時間早晚的問題。
為了解決這個問題,人們提出了廉價磁盤冗余陣列(RAID)的概念。例如,使用兩塊相同容量的磁盤組成 RAID-1 (MIRROR) 陣列,可以在其中任意一塊出現問題時,從另一塊中取出數據。而如果有至少3塊硬盤,便可以組成 RAID-5 (注:還有其他RAID級別可以用3塊硬盤組成冗余結構),只損失 1/n 的容量(n為硬盤數量)來得到帶冗余的存儲,使得存儲可靠性得以提高。
除了改善可靠性之外,RAID還可以用來改善讀寫性能。例如用多塊硬盤組成 RAID-0 陣列,可以將讀寫性能提高 n 倍,等等。我們並不討論這些RAID級別。
不幸會發生
和很多人已經想到的一樣──不要高興的太早......
帶數據冗余的 RAID 的一個基本假設是,磁盤是不騙人的,它有兩種狀態:好、壞,並且,主機(或RAID控制器)能夠可靠地識別這種狀態。
很不幸,這句話只對了一半。一塊磁盤要麼是好的、要麼是壞的(這裡,"壞的"的定義是讀寫時會發生任何錯誤),但是主機未必能夠識別這種狀態。
更為嚴重的是,有些時候主機甚至連讀出來的數據是否是正確的這件事都不知道!當你發現自己的程序在其它機器上都很正常,但是在某台機器上總是神秘的崩潰的時候,你就要看看是不是那台機器的內存或者其他存儲器出現問題了。
經歷了大量痛苦的數據恢復工作之後,終於有人想出了一個主意:把數據的校驗和放到別的地方,例如,引用這塊數據的地方,並且將元數據保存多份。解決問題的方法有很多,一種是花更多的錢去買更好的硬件,而另一種,則是采用一些合理的技術手段來降低硬件發生問題時的影響──你不是不可靠嗎?那麼我檢查你,確認你的數據是對的我才接受;你不是容易丟失數據嗎?那我就多存幾份,多到將真的丟失數據的概率降到可以接受的水平為止。
這種技巧是廣大勞動人民在長期的科研實踐中總結出來的,並且在歷史上有著相當廣泛的應用。例如,傳統 BSD 系統中的伯克利快速文件系統(FFS,在現代 FreeBSD 版本中稱為 UFS)會保存大量的關鍵元數據──超級塊和柱面組映射表的副本,而這些副本在布局的時候,被保證不會放在磁盤的相同盤面、柱面或扇區上,從而保證了磁盤在失去一個盤面、被劃傷一個柱面,或者被子彈擊穿時,仍然會有可用的超級塊副本存在,從而能夠恢復部分數據。
不過,簡單地把所有數據保存多份有時是不需要的。首先,它會影響寫入性能,並損害寫操作的原子性。其次,存儲設備盡管越來越便宜,但它總歸還是要錢的,你不會希望把錢均勻的用在不同的數據上面──舉個例子,你花12個小時寫了一些代碼,這些代碼丟了你會很心疼;但是你的程序有bug,運行5分鐘之後吐了一個2GB的core文件,這個文件丟了,你可能會覺得無所謂,至少沒有丟掉代碼那樣心疼。那麼,假如有3份存儲資源,我想多數人不會給兩種文件各分配1.5份,因為很明顯,給代碼分配2份,而給core文件分配1份,使得代碼數據能夠有更高的冗余度,是比較合算的做法。
對於RAID來說,一個比較常見的做法便是將一部分劃成RAID-0卷,另一部分劃成RAID-5卷。不過,這個做法並不完美,因為你並不能動態調整這兩個卷的尺寸。如果之前規劃的卷尺寸不合適,那只有停機、導出數據、重劃RAID,導入數據,並祈禱下次發現不合適的時間越晚越好。
當然,這是一個極端的例子。對於個人用戶來說,他們往往並不會在意 RAID-0 所帶來的那一點點性能改善,而數據的可靠性才是他們更關注的事情。
個人系統中的數據冗余
雖然 RAID 是廉價磁盤冗余陣列,但是事實上它並不是那麼的廉價。更重要的是,許多便攜式系統,如筆記本計算機,並不具備安裝多塊硬盤的條件(無論是體積還是重量)。這類用戶可能只有一塊硬盤,但是仍然希望盡量提高他們系統中數據的可靠性。這個時候,就需要做一些存儲規劃了。
以我個人的筆記本計算機為例,它安裝的是雙系統,Windows和FreeBSD。平時工作的時候主要使用的是 FreeBSD,工作內容包括代碼編寫和調試、遠程登錄和撰寫文檔等等。
我們可以簡單地把這個系統中的數據分成下面幾類:
操作系統。
第三方軟件(如 X11 視窗系統、Eclipse、OpenOffice辦公系統等)
下載的數據(例如 ports、其 distfiles等等),其特點是來自互聯網,只要帶寬夠,既不需要備份,也不擔心丟失。
用戶數據 (完全不希望丟失的數據)
我使用 ZFS 作為 / 分區(注意:這種做法有好處也有缺點,我們將在最後討論),由於只有一塊硬盤,因此除了用於引導的 UFS 之外,其余部分全部劃給同一個 zpool。
針對不同類型數據的備份和冗余需要,在這個 zpool 上面建立了 4 個 ZFS:
/ - 即 zpool 本身的 ZFS,用於存放操作系統
/.portspace - 保存第三方軟件(為什麼分開放將在後面解釋)
/.download - 保存下載的數據,例如 CVS 代碼庫、ports本身及其distfiles等
/.data - 保存用戶數據,包括 /home,/usr/src,自己工作用的代碼庫等等。這部分數據采取的保存策略是壓縮,同時設置 copies = 2。
之後,做下列調整:
使用符號鏈接將 /usr/local 和 /var/db/pkg 放到 /.portspace 上。這樣做的好處是,在進行 port升級的時候(例如恐怖的OpenOffice、xorg之類的升級),你可以做下面的操作:
zfs snapshot tank/.portspace@preclean
rm -fr /usr/local/* /var/db/pkg/*
或者,如果出現問題,可以用zfs的rollback功能回到快照版本,更變態的用法甚至可以跑branch出來。
關於快照
ZFS可以在文件系統中建立大量的快照。通過腳本可以將快照操作自動化。sysutils/freebsd-snapshot提供了一組易於配置的snapshot工具,我的配置中,對 / 和 /.portspace 每天中午的時候做一次snapshot,保留3份;對 /.download 每逢6的整數倍點做一次snapshot,保留2份;對/.data每小時做一次snapshot,保留25份。
成果
在本次硬盤壞損中,使用recoverdisk恢復了3天,最後有大約3.4MB(IBM記法;最初的結果是有大約2GB數據存在問題,占磁盤總容量的5%)數據未能恢復。
使用zpool scrub共發現650處壞損數據,共導致出現了132個無法恢復的數據塊,這些數據塊分屬於20個文件,/.data 中完全沒有丟失數據。
其他一些問題:
A。是否應使用 ZFS 作為 /?
答:取決於具體情況。ZFS作為/對於安裝、故障恢復都會帶來一些困難,並且其基於快照的revert能力也不能直接使用,只能通過復制或rsync的方法來恢復快照中的內容。
B。磁盤出現故障,通過recoverdisk恢復之後,ZFS說卷degraded,如何恢復?
答:首先zfs export,然後再重新zfs import(可能需要-f參數)。注意此時/boot/zfs必須可寫。
C。ZFS的快照占多少空間?
答:取決於修改的文件數量和數據塊數量。通常快照占用的空間可忽略不計(120K左右),除非此後又翻回頭來狂砍/狂改文件。