當(dāng)今信息化時代,信息就是利潤,數(shù)據(jù)就是企業(yè)的命根子。世界運(yùn)行在數(shù)據(jù)之上。當(dāng)代量子學(xué)的最新理論是:量子其實是一種信息,而不是物質(zhì)。持有這種觀點的量子科學(xué)家認(rèn)為,我們目前的所謂“物質(zhì)”世界,只不過都是反映在人腦中的一種信息,而不是實實在在的物質(zhì)。這就像黑客帝國中的情景一樣,人們以為周圍的環(huán)境都是實實在在的,卻不知自己正生活在一個虛擬世界,這個虛擬世界,由一個超級程序員創(chuàng)造。而這個程序員,就是理想中的上帝。這些科學(xué)家認(rèn)為,只有把所有東西理解成信息,才能解釋量子理論。乍一看,這有點唯心主義。但是唯物主義者就能自圓其說了么?不能。你能肯定你所觸摸到的,所看見的,都是實實在在的所謂“物質(zhì)”么?不能。因為你眼睛所感知到的,只不過是光線,光觸發(fā)了你的視網(wǎng)膜細(xì)胞,產(chǎn)生一系列的生化反應(yīng),蛋白質(zhì)相互作用,神經(jīng)網(wǎng)絡(luò)傳導(dǎo),直到你的大腦中樞,產(chǎn)生一系列的脈沖,一系列的邏輯,在你大腦中產(chǎn)生一個刺激。這一系列的脈沖刺激,就是信息,就是邏輯,因為這樣,所以那樣。如果人為制造出和現(xiàn)實世界相同的光線環(huán)境,來刺激你的眼睛,如果絲毫不差 ,那么你同樣會認(rèn)為你所處的是現(xiàn)實世界,然而,卻不是。一個球體,你看見它是圓的,那是因為它在你大腦中產(chǎn)生的刺激,你認(rèn)為他是圓的,而且可以在平面上平滑滾動,這一系列的性質(zhì),其實也是在你大腦中產(chǎn)生的,是你認(rèn)為他會平滑滾動,而你不能保證,客觀實在情況下,它一定就是平滑滾動。而如果把這個球體拿到一個外星生物,對他進(jìn)行刺激,它可能會“看”到,這個東西,是個正方體,或者是個無規(guī)則形狀的東西。它不會平滑滾動,只能一蹦一蹦的滾動。為什么兩種生物可能會對一個東西產(chǎn)生不同的概念呢?很簡單,因為他們大腦組成不同,生化反應(yīng)進(jìn)行的不同,蛋白質(zhì)相互作用不同,所以產(chǎn)生的邏輯不同,產(chǎn)生的刺激就不同。為什么會產(chǎn)生色盲?因為色盲的視網(wǎng)膜產(chǎn)生的邏輯和正常人不同,所以產(chǎn)生不同的刺激。當(dāng)然如果一種顏色,比如紅色,對一個人產(chǎn)生的刺激,比如抽象的叫做刺激A,而對另一個人產(chǎn)生的刺激,叫做刺激B,但是如果從小就接受顏色訓(xùn)練,就把這種刺激叫做“紅色”,不管他是刺激A還是刺激B,那么對正常交流就不會有影響,也就是不同的刺激而已,但是如果接受到的教育,是把這種刺激叫做“綠色”,那么就和其他大多數(shù)人不一樣了,這是第一種色盲,可以說這種所謂“色盲”完全是由于后天教育不同而造成的,可以很容易的改正,只要告訴他,你看到的這種信息,這種刺激,用語言表達(dá)的話,應(yīng)該叫做“紅色”,你小時候,父母對你說錯了。第二種色盲,就是果真色盲了,因為它看到兩種顏色,對他的刺激都是相同的,因為他的視網(wǎng)膜上缺少某種生化反應(yīng),或者缺少某種蛋白質(zhì),也就造成了他缺少區(qū)分這兩種刺激的邏輯。這種色盲,是不可逆的,因為刺激相同,已經(jīng)無法區(qū)分了?


    通過上面的論述,我們能初步認(rèn)識到:所謂“物質(zhì)”,最后都是通過信息來表現(xiàn),你永遠(yuǎn)不知道理想中的所謂“客觀真實”,他到底是什么東西,永遠(yuǎn)不知道。所謂“不識廬山真面目,只緣身在此山中”,這個道理,古人早就知道了。唯一知道的,就是那個超級程序員,也就是“上帝”。一個東西,在生物眼中,或者觸摸感覺中,都是一串電脈沖,都是邏輯,都是信息??梢哉f,世界在生物眼中就是信息,世界通過信息來反映,脫離了信息,“世界”什么都不是。說到這里,我們完全迷茫了。我們所看到的東西,到底是世界的刺激,還是一場虛幻的刺激?就像玩3D仿真游戲一樣,你所看到的,也許只是一場虛幻的刺激,而不是真實世界的刺激。每當(dāng)想到這里,我不自主的產(chǎn)生一種渺小感,一種失落感,感覺生命已經(jīng)失去它所存在的意義。每當(dāng)看見我的身體,我的手腳,它可能只是虛幻的,它只是在刺激你的大腦而已,你割一刀,會產(chǎn)生一個疼痛的刺激,就這么簡單的邏輯。


    還是那句話,“不識廬山真面目,只緣身在此山中”。不管是否是虛幻的,我們還是要按照程序邏輯,餓了要找飯吃,困了會打瞌睡,不管這些邏輯是真實存在,還是虛幻的由超級程序員設(shè)定的,我們只能遵循它,餓了不吃飯,會餓死,困了不睡覺,也會痛苦。而這些邏輯,同樣也是程序。從這種層面上來看,制造出人工智能是完全可能的,只不過我們還沒有掌握上帝的編程技巧。


    說到這里我們可能隱約想到,也許上帝也是按照“上帝的上帝”創(chuàng)造的邏輯,來創(chuàng)造他自己的“人工智能”,也就是我們。這樣生生不息,輪回往復(fù)??催^影片《人工智能》的都了解,最后世界被我們創(chuàng)造出來的機(jī)器人所替代。惡劣的環(huán)境,已經(jīng)不再適合肉體人類生存了。人類把自己的思想,賦予了一堆電路,一堆機(jī)器,讓他們延續(xù)自己的生命。


    我在這里做一個預(yù)言:人類賦予他們的程序,也許隨著環(huán)境的變化,有一天也不再適合他們。所以他們迫切需要進(jìn)化,他們的邏輯電路,也可以進(jìn)化,即如果某些電路失效,或者短路之類的,會產(chǎn)生一些奇特的邏輯,不斷進(jìn)化。當(dāng)一個機(jī)器人機(jī)械老化的時候,按照程序,他制造出新的機(jī)器,將自己的邏輯電路,復(fù)制到新的機(jī)器上,延續(xù)生命,然后新的機(jī)器再不斷進(jìn)化。


    什么是數(shù)據(jù)


    信息是如此重要,以至于人們對它非常重視。如果失去了物質(zhì),沒什么,但是如果失去了信息,那么一切都就消逝了。所以人們想出一切辦法,使這些信息能保存下來。要把一種邏輯刺激保存下來,我們知道,一切都是信息,那么保存下來的東西,也是信息,只不過是一種描述信息的信息,這種信息,叫做數(shù)據(jù)。數(shù)據(jù)包含了信息,讀入數(shù)據(jù),就產(chǎn)生信息。也就是讀入一種信息,產(chǎn)生另一種信息表示。數(shù)據(jù)是可以保存在一種物質(zhì)上的,這種物質(zhì)對計算機(jī)的刺激,就產(chǎn)生了信息,而這些信息繼而再對人腦產(chǎn)生刺激,最終決定了我們?nèi)祟惖男袨?。也就是?shù)據(jù)影響人類的行為!


    說到這里,我們看出了數(shù)據(jù)的重要性!它是整個人類發(fā)展的重要決定因素。如果數(shù)據(jù)被破壞,或者篡改,那么就會影響到人類的發(fā)展。比如一個控制核爆炸的程序,一旦被篡改,那么后果將會不堪設(shè)想。按照我們的結(jié)論,一切都是信息,核爆炸也是一種信息,能被感覺到,才是信息,也就是說,對于一個感覺不到任何刺激的人來說,核爆炸,也不算什么災(zāi)難了,當(dāng)然感覺不到刺激的人,稱不上人,植物人也能感覺到刺激。


    整個世界,可以說是信息之間的相互作用。信息影響信息。數(shù)據(jù)如此重要,所以人們想出一切辦法來保護(hù)這些收據(jù)。將信息放在另一種信息上。比如把數(shù)據(jù)放在磁盤上。數(shù)據(jù)存放在磁盤上,需要有一定的組織,組織數(shù)據(jù),這個任務(wù)由文件系統(tǒng)來但當(dāng)。


    數(shù)據(jù)存儲


    文件系統(tǒng),其實是一段代碼,這段代碼本身也是信息,也要存儲在磁盤上。不僅僅代碼要存在磁盤上,而且代碼也要通過讀取一些信息,才能完成功能,這些信息,就是文件系統(tǒng)元數(shù)據(jù),也就是用來描述文件系統(tǒng)結(jié)構(gòu)的數(shù)據(jù)。這些元數(shù)據(jù)也是以文件的形式存放在磁盤上。用文件來描述文件,和用信息來描述信息,他們是歸一的,正像用智能來創(chuàng)造智能一樣!


    關(guān)于文件系統(tǒng)的詳細(xì)模型描述,請參閱《存儲秘史》。


    數(shù)據(jù)保護(hù)


    數(shù)據(jù)保護(hù),就是需要對當(dāng)前磁盤上的數(shù)據(jù),進(jìn)行備份,以防突如其來的磁盤損壞,或者其他各種原因?qū)е碌臄?shù)據(jù)不可被訪問,或者部分?jǐn)?shù)據(jù)已經(jīng)損壞,已經(jīng)影響到了業(yè)務(wù)層。備份后的數(shù)據(jù),可以在數(shù)據(jù)失敗之后,第一時間恢復(fù)到生產(chǎn)磁盤上,從而最大程度地減少損失。


    數(shù)據(jù)保護(hù)的方法


    從底層來分,數(shù)據(jù)保護(hù)備份可以分為文件級的保護(hù)和塊級的保護(hù)。


    文件級備份


    如果備份軟件將文件備份到磁盤介質(zhì)或者任何其他的塊介質(zhì)上,那么這些文件就可以是不連續(xù)的,塊設(shè)備可以跳躍式的記錄數(shù)據(jù),而一個完整數(shù)據(jù)鏈信息,由管理這種介質(zhì)的文件系統(tǒng)來記錄。磁盤讀寫速度比磁帶要高的多。


    近年來出現(xiàn)了VTL,即Virtual Tape Library,虛擬磁帶庫,即用磁盤來模擬磁帶。乍一看比較新鮮,其實實現(xiàn)起來,還是在代碼上做改動即可。欺騙上層底層物理介質(zhì)是磁帶,然后自己再按照磁盤的記錄方式讀寫數(shù)據(jù),這就是虛擬化的表現(xiàn)。這種方法,提高了速度,用處不小。


    數(shù)據(jù)保護(hù)并不是陽春白雪,我們經(jīng)常用的賽門鐵克公司的Ghost,就是一種文件備份軟件。他將一個分區(qū)或者整塊磁盤上的文件,及磁盤分區(qū)表,MBR等信息一同備份,打包成一個大文件,系統(tǒng)故障的時候,就可以用軟件來讀取這個文件,向磁盤中做恢復(fù)。Ghost支持多種文件系統(tǒng),包括linux的ext2。Veritas,CA等等廠家都有自己的文件級備份軟件解決方案。


    塊級備份


    文件級的備份,即備份軟件只能感知到文件這一層,將磁盤上所有的文件,備份到另一個介質(zhì)上。所以文件級備份軟件,要么依靠操作系統(tǒng)提供的文件系統(tǒng)接口來備份文件,要么自己具有文件系統(tǒng)的功能,可以識別文件系統(tǒng)元數(shù)據(jù)。文件級備份軟件的基本機(jī)制,就是將數(shù)據(jù)以文件的形式讀出,然后再將讀出的文件存儲在另外一個介質(zhì)上。這些文件,在原來的介質(zhì)上,存放可以是不連續(xù)的,各個不連續(xù)的塊之間的鏈關(guān)系由文件系統(tǒng)來管理。而如果備份軟件將這些文件,備份到磁帶介質(zhì)上,那么這些文件必須是連續(xù)的,因為磁帶不是塊設(shè)備,由于機(jī)械限制,他記錄數(shù)據(jù)的時候,是連續(xù)的。磁帶上的數(shù)據(jù),也需要組織,相對于磁盤文件系統(tǒng),也有磁帶文件系統(tǒng),準(zhǔn)確來說不應(yīng)該叫做磁帶文件系統(tǒng),而應(yīng)該叫做磁帶數(shù)據(jù)管理系統(tǒng)。因為對于磁帶來說,它沒有文件的概念,它記錄的數(shù)據(jù)都是流式的,連續(xù)的。數(shù)據(jù)之間用一些特殊的間隔來分割,從而可以區(qū)分一個個的“文件”,其實就是一段段的二進(jìn)制數(shù)據(jù)流。因為磁帶設(shè)備平時幾乎應(yīng)用不到,所以一般操作系統(tǒng)中不會自帶這種磁帶數(shù)據(jù)管理系統(tǒng),而只有備份軟件,才帶有這種功能。磁帶備份文件的時候,會將磁盤上每個文件的屬性信息,和實體文件數(shù)據(jù)一同備份下來,但是不會備份磁盤文件系統(tǒng)的描述信息,比如一個文件所占用的磁盤簇號鏈表等等,因為利用磁帶恢復(fù)數(shù)據(jù)的時候,軟件會重構(gòu)磁盤文件系統(tǒng),并從磁帶讀出數(shù)據(jù),向磁盤寫入數(shù)據(jù)。


    這里說一個題外話,就是數(shù)字磁帶和模擬磁帶的區(qū)別。2005年之前,大批的人都帶著隨身聽,里面裝一盤磁帶,掛著耳機(jī)。06年之后,好像再也沒看到過帶隨身聽的人,都換成了MP3,MP4了。這個現(xiàn)象就發(fā)生在我們身邊。隨身聽用的是模擬磁帶,也就是他記錄的是模擬信號,電流強(qiáng),磁化的就強(qiáng),電流弱,磁化的就弱,磁轉(zhuǎn)成電的時候也一樣,用這種磁信號強(qiáng)弱信息來表達(dá)聲音震動的強(qiáng)弱信息,從而形成音樂。MP3則是利用數(shù)字信息來記錄聲音震動強(qiáng)弱信息。雖然由模擬轉(zhuǎn)向數(shù)字,需要數(shù)字采樣轉(zhuǎn)換,音樂的質(zhì)量相對模擬信號來的差,算法也復(fù)雜,但是他具有極大的抗干擾能力,而且可以無縫的和計算機(jī)結(jié)合,形成能發(fā)聲的計算機(jī)(多媒體計算機(jī))。錄音帶,錄像帶,都是模擬信號磁帶。用于文件備份的磁帶,當(dāng)然是數(shù)字磁帶,他記錄的是磁性的極性,而不是被磁化的強(qiáng)弱,比如用N極來代表1,用S極來代表0。


    所謂塊級的備份,就是備份塊設(shè)備上的每個塊,不管這個塊上有沒有數(shù)據(jù),或者這個塊上的數(shù)據(jù)屬于哪個文件。塊級別的備份,不考慮文件,原設(shè)備有多少容量,就備份多少容量。在這里,“塊”這個概念,對于磁盤來說,就是扇區(qū),sector。塊級的備份,是最低層的備份,他拋開了文件系統(tǒng),直接對磁盤扇區(qū)進(jìn)行讀取,并將讀取到的扇區(qū)寫入新的磁盤對應(yīng)的扇區(qū)。


    這種方式的實例,比如磁盤鏡像,就是一個很好的例子。比如RAID1,對一塊磁盤的讀寫,完全復(fù)制一份到另外的磁盤,兩塊磁盤內(nèi)容完全相同。再比如一些數(shù)據(jù)恢復(fù)公司的一些專用設(shè)備,磁盤復(fù)制機(jī),也是直接讀取磁盤扇區(qū),然后拷貝到新的磁盤。


    這些備份軟件,不經(jīng)過操作系統(tǒng)的FS接口,而是直接通過磁盤控制器驅(qū)動接口,直接讀取磁盤,所以相對文件級的備份來說,速度有所加快,但是其備份的數(shù)量相對文件級備份要多,會備份許多空扇區(qū),而且備份之后,原來不連續(xù)的文件,備份之后還是不連續(xù),有很多碎片。文件級的備份,會將原來不連續(xù)的文件,備份成連續(xù)存放的文件,恢復(fù)的時候,也會在原來的磁盤上連續(xù)寫入,所以很少造成碎片。有很多系統(tǒng)管理員,都會定時將系統(tǒng)備份并重新導(dǎo)入一次,就是為了剃除磁盤碎片,其實這么做的效果和磁盤碎片整理程序效果一樣,但是速度確比后者快得多。


    高級數(shù)據(jù)保護(hù)方法


    遠(yuǎn)程文件復(fù)制


    這種方案,即把備份的文件,通過網(wǎng)絡(luò)傳輸?shù)疆惖厝轂?zāi)站點。典型的代表是rsync異步遠(yuǎn)程文件同步軟件。這是一個運(yùn)行在linux下的文件遠(yuǎn)程同步軟件。他監(jiān)視文件系統(tǒng)的動作,將文件的變化,通過網(wǎng)絡(luò),同步到異地的站點。他可以只復(fù)制一個文件中變化過的內(nèi)容,而不必整個文件都復(fù)制,這在同步大文件的時候非常管用。


    遠(yuǎn)程磁盤鏡像


    這是基于塊的遠(yuǎn)程備份。即通過網(wǎng)絡(luò),將備份的數(shù)據(jù)傳輸?shù)疆惖卣军c。有可以分為同步復(fù)制,和異步復(fù)制。同步復(fù)制,即主站點接受的上層IO數(shù)據(jù),必須等待傳輸?shù)疆惖卣军c之后,才通報上層IO成功消息。異步復(fù)制,就是上層IO,主站點寫入成功,即向上層通報成功,然后后臺將數(shù)據(jù)通過網(wǎng)絡(luò)傳輸?shù)疆惖亍G罢吣鼙WC兩地數(shù)據(jù)的一致性,但是對上層響應(yīng)較慢。而后者不能實時保證兩地數(shù)據(jù)的一致性,但是對上層響應(yīng)很快。


    所有基于塊的備份措施,一般都是在底層設(shè)備上進(jìn)行,而不耗費主機(jī)資源。


    盤陣廠家的中高端產(chǎn)品,都提供遠(yuǎn)程鏡像服務(wù),比如IBM的PPRC,EMC的SRDF,HDS的Truecopy等等。


    照數(shù)據(jù)保護(hù)


    遠(yuǎn)程鏡像,或者本地鏡像,確實是對生產(chǎn)卷數(shù)據(jù)的一種很好的保護(hù),一旦生產(chǎn)卷故障,可以立即切換到鏡像卷。但是這個鏡像卷,一定要保持一直在線狀態(tài),主卷有寫IO操作,那么鏡像卷也逃不掉。如果此時想某一時刻的整個系統(tǒng),進(jìn)行備份,在鏡像的環(huán)境中,就只能停止應(yīng)用,使應(yīng)用不再對卷產(chǎn)生IO操作,然后將鏡像關(guān)系分離,稱作拆分鏡像,拆分之后,可以恢復(fù)上層的IO。此時的鏡像卷,就是主機(jī)停止IO那一刻的數(shù)據(jù)完整鏡像,此時可以用備份軟件,將鏡像卷上的數(shù)據(jù),備份到其他介質(zhì)。拆分鏡像,是為了讓鏡像卷可以被備份軟件操作。拆分之后,主卷所做的所有寫IO,會以bitmap的方式記錄下來(bitmap的概念,請參考《存儲秘史》文件系統(tǒng)相關(guān)知識),待備份完成之后,可以將鏡像關(guān)系恢復(fù),此時主卷和鏡像卷上的數(shù)據(jù)是不一致的,需要重新做同步。


    可以看到,以上的過程是十分復(fù)雜繁瑣的,而且需要占用一塊和主卷相同容量大小的卷。關(guān)鍵是需要停掉主機(jī)IO,這對應(yīng)用會產(chǎn)生影響。為了解決這個難題,一種解決方案出現(xiàn)了,這就是快照技術(shù)。快照的基本思想是,抓取某一時間點,磁盤的所有數(shù)據(jù),就像急凍一樣,但是還不能真正凍住,主機(jī)的IO需要正常執(zhí)行。這怎么可能呢?快照就將其變成了可能??煺帐沁@樣實現(xiàn)的,即先在某一時間點,對于這個時間點之后的所有上層的寫IO,先將這個IO對應(yīng)的塊上的數(shù)據(jù)復(fù)制到一個新的卷中存放,并做好原卷中的這個塊和新卷中塊的對應(yīng)關(guān)系記錄,然后才進(jìn)行上層IO的寫入。這樣,這一時間點上磁盤的數(shù)據(jù),便被保存下來,就像做了急凍一樣。這種方法也叫做copy on write,也就是在發(fā)生IO寫之前,先將待更新塊中原來的數(shù)據(jù)復(fù)制出來保存,然后再做新數(shù)據(jù)的寫入,即寫時復(fù)制。還有一種實現(xiàn)塊快照的方法,叫做write redirect,當(dāng)寫IO到來的時候,將這個IO重定向到一個新的卷,而不是寫原來的卷,并做好新卷上的塊和原來卷所應(yīng)該被寫入塊的映射記錄。這樣也同樣保存下了這個時刻原來卷上的所有數(shù)據(jù),同時不影響后續(xù)讀寫IO操作,因為保持了塊映射關(guān)系。


    在“照”下了這一時刻卷上的數(shù)據(jù)之后,為了保險起見,最好對那個時刻的數(shù)據(jù)做一個備份,也就是將這些數(shù)據(jù)在復(fù)制到另外的磁盤或者磁帶中。但是也可以不復(fù)制,而那時的數(shù)據(jù)依然會存在,直到手動刪除這個快照。如果不對快照做備份,那么一旦此時卷數(shù)據(jù)失敗,快照的數(shù)據(jù)也不復(fù)存在。


    不管是copy on write還是write redirect,只要上層有寫IO,這個IO塊就要占用新卷上的一個塊(因為要保留原塊的內(nèi)容,不能被覆蓋),如果上層將原卷上的所有數(shù)據(jù)塊都寫更新了,那么新卷的容量就需要和原卷的數(shù)據(jù)量同樣大,甚至還大(預(yù)防新增數(shù)據(jù)寫入),才可以。但是通常應(yīng)用不會寫覆蓋面百分之百,做快照的時候,新卷的容量一般設(shè)置成原卷容量的30%就可以。


    實際中一般都是用copy on write的方式做快照,因為write redirect方式,每次寫IO都需要查一遍快映射表,速度慢,耗費資源大。


    值得說明的是,快照所凍結(jié)下來的卷數(shù)據(jù),無異于一次意外掉電之后卷上的數(shù)據(jù)。為什么這么說呢?我們可以比較一下,意外斷電同樣是保持了斷電那個時間點上的卷數(shù)據(jù)狀態(tài)。我們知道,不管是上層應(yīng)用,還是文件系統(tǒng),都有自己的緩存,文件系統(tǒng)緩存的是文件系統(tǒng)元數(shù)據(jù)。并不是每次數(shù)據(jù)的交互,都保存在磁盤上,它們可以暫時保存在內(nèi)存中,然后每隔一段時間(linux系統(tǒng)通常為30秒),批量flush到磁盤上。當(dāng)然編程的時候也可以將每次對內(nèi)存的寫,都flush到磁盤,但是這樣做效率和速度打了折扣。而且當(dāng)flush到磁盤的時候,并不是只做一次IO,如果數(shù)據(jù)量大,會對磁盤做多次IO,如果快照生成的時間恰恰在這連續(xù)的IO之間生成,那么此時卷上的數(shù)據(jù),實際上是有可能不一致的。磁盤IO是原子操作(atomic operation),而上層的一次事物操作,可以對應(yīng)底層的多次原子操作,這其中的一次原子操作,沒有業(yè)務(wù)意義,只有上層的一次完整的事物操作,才有意義。所以如果恰好在一個事物操作對應(yīng)的多個原子操作的中間,生成快照,那么此時的快照數(shù)據(jù),就是不完整的,不一致的。文件系統(tǒng)的機(jī)制是這樣的,它總是先寫入文件的實體數(shù)據(jù)到磁盤,而文件的元數(shù)據(jù),暫不寫到磁盤,而是先寫入緩存中。這種機(jī)制是有他的考慮的,我們想一攏?如果FS先把元數(shù)據(jù)寫入磁盤,而在準(zhǔn)備寫入文件實體數(shù)據(jù)的時候,突然斷電了,那么此時磁盤上的數(shù)據(jù)是這么一個狀態(tài):FS元數(shù)據(jù)中有這個文件的信息,但是實體數(shù)據(jù)并沒有被寫入對應(yīng)的扇區(qū),那么這些對應(yīng)的扇區(qū)上原來的數(shù)據(jù),便會被認(rèn)為就是這個文件的數(shù)據(jù),這顯然后果不堪設(shè)想。所以FS一定是先寫入文件實體數(shù)據(jù),完成之后再批量將元數(shù)據(jù)從緩存中flush到磁盤,如果在實體數(shù)據(jù)寫入磁盤,而元數(shù)據(jù)還沒有寫入磁盤之前,斷電,那么雖然此時文件實體數(shù)據(jù)在磁盤上,但是元數(shù)據(jù)沒有在磁盤上,也就是說雖然有你這個人存在,但是你沒有身份證,那么你就不能公開的進(jìn)行社會活動,因為你不是這個國家的公民。雖然文件系統(tǒng)這么做,會丟失數(shù)據(jù),但是總比向應(yīng)用提交錯誤的數(shù)據(jù)強(qiáng)!大家可以做一個實驗,就拿Windows來說,你創(chuàng)建一個文件,創(chuàng)建好之后,立即斷電,重啟之后,會發(fā)現(xiàn)剛才創(chuàng)建的文件沒了,或者你復(fù)制一個小文件,完成后立即斷電,重啟之后也會發(fā)現(xiàn),復(fù)制的文件不見了,為什么?明明創(chuàng)建好的,文件也復(fù)制好的,為什么斷電重啟就沒了呢?原因很簡單,因為你斷電的時候,F(xiàn)S還沒有把元數(shù)據(jù)flush到磁盤上,你就給斷電了,此時文件實體數(shù)據(jù)雖然還在,但是元數(shù)據(jù)中沒有,那么當(dāng)然看不到它了。


    總之,快照極有可能生成一份存在不一致的卷數(shù)據(jù)。這也沒有辦法,如果用這份數(shù)據(jù)做恢復(fù),那么就必須承擔(dān)數(shù)據(jù)不一致的風(fēng)險。最保險的備份,就是將主機(jī)停機(jī),此時存儲上的數(shù)據(jù),一定是一致的。但是誰能忍受停機(jī)所帶來的損失?所以只能在停機(jī)和一致性之間找一個平衡點。而快照是最方便的。


    有些快照解決方案,會在主機(jī)上安裝一個代理軟件,當(dāng)執(zhí)行快照之前,代理會通知應(yīng)用或者文件系統(tǒng)將緩存中的數(shù)據(jù)全部flush到磁盤,然后立即生成快照,這樣,一致性就得到了保護(hù)。不過相應(yīng)的也耗費了一定的主機(jī)資源和網(wǎng)絡(luò)資源。


    Continuos Data Protect(CDP,連續(xù)數(shù)據(jù)保護(hù))


    SNIA對于CDP給出了如下的定義:持續(xù)數(shù)據(jù)保護(hù)(CDP)是這樣一種在不影響主要數(shù)據(jù)運(yùn)行的前提下,可以實現(xiàn)持續(xù)捕捉或跟蹤目標(biāo)數(shù)據(jù)所發(fā)生的任何改變,并且能夠恢復(fù)到此前任意時間點的方法。CDP系統(tǒng)能夠提供塊級、文件級和應(yīng)用級的備份。


    有一類所謂Near CDP產(chǎn)品,這類產(chǎn)品,一般都是生成高頻率的快照而已,比如一小時幾十次,上百次等等。用這種方法來保證數(shù)據(jù)恢復(fù)的粒度足夠細(xì)。


    快照,每做一次快照,只能保存那個時間點卷上的數(shù)據(jù)狀態(tài),快照之后的卷數(shù)據(jù)不會被保存下來。CDP是這樣一種機(jī)制,即它可以保護(hù)從某時刻開始,卷或者文件在任意此后的時刻的數(shù)據(jù)狀態(tài),也就是數(shù)據(jù)的每次改變,都會被記錄下來,無一遺漏。這個機(jī)制乍一看非常神奇,其實它的底層只不過是比快照多了一些考慮而已,下面我們就來分析它的實現(xiàn)原理。


    文件級的CDP


    顧名思義,文件級CDP,就是通過調(diào)用文件系統(tǒng)的相關(guān)函數(shù),監(jiān)視文件系統(tǒng)動作,文件的每一次變化,都會被記錄下來。這個功能是分析應(yīng)用對文件系統(tǒng)的IO數(shù)據(jù)流,然后計算出文件變化的部分,將其保存在CDP倉庫設(shè)備(存放CDP數(shù)據(jù)的介質(zhì))中。每次對文件的改變,都會被記錄下來??梢詫σ粋€文件,或者一個目錄,甚至一個卷來監(jiān)控。文件級的CDP方案,一般需要在生產(chǎn)主機(jī)上安裝代理,用來監(jiān)控文件系統(tǒng)IO,并將變化的數(shù)據(jù)信息傳送到CDP倉庫介質(zhì)中。文件級的CDP,能夠保證數(shù)據(jù)的一致性。因為他是作用于文件系統(tǒng)層次,捕獲的是完整事物。


    塊級的CDP


    塊級的CDP,就是捕獲底層卷的寫IO變化,并將每次變化的塊數(shù)據(jù)保存下來。我們在這里不探討具體產(chǎn)品的架構(gòu),而只對其底層原理,作一個細(xì)致的描述。


    CDP起源于linux下的CDP模塊。它持續(xù)地捕獲所有I/O請求,并且為在這些請求打上時間戳標(biāo)志。它將數(shù)據(jù)變化以及時間戳保存下來,以便恢復(fù)到過去的任意時刻。


    在linux的CDP實現(xiàn)中,包含下列三個設(shè)備:
    主機(jī)磁盤設(shè)備(host disk)
    CDP倉庫設(shè)備(repository)
    CDP元數(shù)據(jù)設(shè)備(metadata)
    CDP代碼對機(jī)磁盤設(shè)備在任意時刻所作的寫操作都記錄下來,實體數(shù)據(jù)順序?qū)懭隒DP倉庫設(shè)備中,對于這些實體數(shù)據(jù)塊的描述信息,則被寫入到CDP元數(shù)據(jù)設(shè)備的對應(yīng)扇區(qū)。


    元數(shù)據(jù)包含以下信息:
  struct metadata {
   int hrs, min, sec; 該數(shù)據(jù)塊被寫入主機(jī)磁盤設(shè)備的時間;
   unsigned int bisize; 該數(shù)據(jù)塊的以字節(jié)為單位的長度;
   sector_t cdp_sector; CDP倉庫設(shè)備中對應(yīng)數(shù)據(jù)塊的起始扇區(qū)編號;
   sector_t host_sector; 該數(shù)據(jù)塊在主機(jī)磁盤設(shè)備中的起始扇區(qū)編號;
  };


    下圖反映了主機(jī)磁盤設(shè)備和CDP倉庫設(shè)備之間的關(guān)系。CDP倉庫設(shè)備中按時間順序保存了對主機(jī)磁盤設(shè)備的數(shù)據(jù)修改。A為主機(jī)磁盤設(shè)備上的一個扇區(qū),該扇區(qū)在9:00和9:05分別進(jìn)行了修改,它在CDP倉庫設(shè)備中對應(yīng)的扇區(qū)分別為A1和A2。



    下圖反映了CDP倉庫設(shè)備和CDP元數(shù)據(jù)設(shè)備之間的關(guān)系,它們以寫入順序一一對應(yīng)。CDP倉庫設(shè)備中的一個元數(shù)據(jù)對應(yīng)CDP元數(shù)據(jù)設(shè)備中一個I/O請求,實際上可能是多個扇區(qū)。具體扇區(qū)數(shù)由元數(shù)據(jù)中的bisize指定,而起始扇區(qū)位置由cdp_sector指定。



    全局變量maddr保存了下一個I/O請求在CDP倉庫設(shè)備上執(zhí)行的地址(起始扇區(qū)編號)。maddr的初值被定義為宏START_METADATA(0)。
   unsigned int maddr = START_METADATA;


    當(dāng)一個寫請求到來時,對應(yīng)數(shù)據(jù)被寫到CDP倉庫設(shè)備中,這時所作的操作如下:
   將寫入CDP倉庫設(shè)備的數(shù)據(jù)塊起始扇區(qū)編號設(shè)置為maddr;
   根據(jù)要寫入主機(jī)磁盤設(shè)備的數(shù)據(jù)塊的扇區(qū)數(shù)目增加maddr。
   這時,我們要將這里寫入的CDP倉庫設(shè)備的數(shù)據(jù)塊編號記錄下來以便構(gòu)造對應(yīng)的元數(shù)據(jù)。


    CDP元數(shù)據(jù)設(shè)備


    全局變量taddr保存了下一個I/O請求對應(yīng)的元數(shù)據(jù)在CDP元數(shù)據(jù)設(shè)備中保存的地址(起始扇區(qū)編號)。 taddr的初值被定義為宏START_METADATA(0)。
     unsigned int taddr = START_METADATA;
     當(dāng)一個寫請求到來時,對應(yīng)的元數(shù)據(jù)被記錄在CDP元數(shù)據(jù)設(shè)備中。
     為了簡單起見,在元數(shù)據(jù)設(shè)備上,一個扇區(qū)(512字節(jié))只保存一個元數(shù)據(jù)信息(只有32字節(jié)),這樣浪費了大量的存儲空間,但對元數(shù)據(jù)設(shè)備的處理卻非常簡單:
     將寫入CDP元數(shù)據(jù)設(shè)備的元數(shù)據(jù)起始扇區(qū)編號設(shè)置為taddr,長度為1個扇區(qū);
     將taddr增1。
     請求處理過程
     請求處理過程是從make_request函數(shù)開始的??紤]到讀請求的處理的相似性,甚至更為簡單,我們這里只分析對寫請求的處理過程。我們首先獲得當(dāng)前的系統(tǒng)時間。之后,寫請求bio結(jié)構(gòu)(為說明方便,我們記為B)被分為三個寫請求bio結(jié)構(gòu)(分別為B0、B1和B2)。這三個bio結(jié)構(gòu)的作用是:
   B0:將數(shù)據(jù)塊寫到主機(jī)磁盤設(shè)備;
   B1:將數(shù)據(jù)塊寫到CDP倉庫設(shè)備;
   B2:將元數(shù)據(jù)寫到CDP元數(shù)據(jù)設(shè)備。
    同其它塊設(shè)備驅(qū)動程序的實現(xiàn)一樣。我們從B克隆產(chǎn)生B0、B1和B2。然后重定向它們要處理的設(shè)備,即bi_bdev域。另外一個大的變動是重新設(shè)置了bi_end_io域,用于在I/O請求完成之后進(jìn)行善后處理。



    為了處理善后,還將B0、B1和B2的bi_private指向同一個cdp_bio1結(jié)構(gòu)。從這個結(jié)構(gòu),我們要能夠回到對B的處理。
  struct cdp_bio {
   struct bio *master_bio; 原來的bio,通過這個域我們可以從B0、B1、B2找到B
   struct bio *bios[3]; 如果IO為WRITE,這個指針數(shù)組分別指向B0、B1、B2,為何需要這個域?
   atomic_t remaining; 這里一個計數(shù)器,我們后面將解釋。
   unsigned long state; 在I/O完成方法中使用
  };
    善后工作的主要目的是:在B0、B1和B2都執(zhí)行完成后,回去執(zhí)行B,為此,我們需要一個“have we finished”計數(shù)器,這就是原子整型變量remaining。在構(gòu)造B0、B1、B2時分別遞增,同時在B0、B1和B2的I/O完成方法中遞減,最后根據(jù)該值是否遞減到0,來判斷B0、B1和B2是否都已經(jīng)執(zhí)行完畢。為了防止B0在構(gòu)造后,在B1和B2構(gòu)造之前就執(zhí)行到B0的I/O完成方法,從而使得remaining變成0,這種錯誤情況。我們沒有將remaining的初值設(shè)置為0,而是設(shè)為1。并在B0、B1、B2都構(gòu)造完成執(zhí)行遞減一次。
  B0、B1、B2都執(zhí)行完成之后,進(jìn)行如下的處理:
  調(diào)用B的善后處理函數(shù);
  釋放期間分配的數(shù)據(jù)結(jié)構(gòu);
  向上層buffer cache返回成功/錯誤碼。


    另一個需要說明的是對B2的構(gòu)造,這個bio結(jié)構(gòu)需要處理的是元數(shù)據(jù)。時間戳已經(jīng)在進(jìn)入make_request時獲得了保存,而對主機(jī)磁盤設(shè)備操作的起始扇區(qū)和長度從B中可以獲得,對應(yīng)的CDP倉庫和CDP元數(shù)據(jù)的起始地址分別保存在全局變量maddr和taddr中。


    數(shù)據(jù)恢復(fù)過程


    我們可以將數(shù)據(jù)恢復(fù)到以前的任意時刻。CDP實現(xiàn)代碼中提供了一個blk_ioctl函數(shù),用戶空間以GET_TIME為參數(shù)調(diào)用該函數(shù),將主機(jī)磁盤設(shè)備中的數(shù)據(jù)恢復(fù)到指定的時間點?;謴?fù)的過程分為兩步:


  1. 順序讀取CDP元數(shù)據(jù)設(shè)備的所有扇區(qū),構(gòu)造一個從主機(jī)磁盤設(shè)備數(shù)據(jù)塊到CDP倉庫設(shè)備的(在這個時間點之前)更新數(shù)據(jù)塊的映射。其結(jié)果保存在以mt_home為首的(映射表)鏈表中。



    這里需要構(gòu)造taddr個對CDP元數(shù)據(jù)設(shè)備的讀請求,每個請求讀取一個扇區(qū)。在這些請求的I/O完成方法中,從讀到的數(shù)據(jù)中構(gòu)造元數(shù)據(jù),并遞減計數(shù)器count。


    如果元數(shù)據(jù)中的時間戳早于或等于指定的恢復(fù)時間點,則需要添加或修改mt_home鏈表的元數(shù)據(jù)結(jié)構(gòu)。需要說明的是,這些項是以host_sector為關(guān)鍵字索引的,因此添加或修改取決于前面是否出現(xiàn)對同一個host_sector的修改。我們以順序方式讀取的過程中,可以保證host_sector(在指定的恢復(fù)時間點之前)的最新修改cdp_sector會出現(xiàn)在這個鏈表中。


    由于計數(shù)器count為taddr,如果它遞減為0,說明CDP元數(shù)據(jù)設(shè)備中的所有數(shù)據(jù)均已讀出并處理,這時我們可以繼續(xù)往后面執(zhí)行。


    2. 從CDP倉庫設(shè)備中讀取這些更新的數(shù)據(jù)塊,構(gòu)造以mt_bi_home為首的鏈表。


    同上面的處理類似,我們需要為mt_home鏈表中的每一項構(gòu)造對CDP倉庫設(shè)備的讀請求,每個請求在CDP倉庫設(shè)備的起始編號取決于cdp_sector域,長度則根據(jù)bisize而定。這個請求讀出的數(shù)據(jù)需要被寫入到主機(jī)磁盤設(shè)備中,為此我們在讀請求I/O完成函數(shù)中,構(gòu)造一個對應(yīng)的往主機(jī)磁盤設(shè)備的寫請求bio,該寫請求的起始編號取決于host_sector域,長度根據(jù)bisize而定,而要寫入的數(shù)據(jù)是剛剛從CDP倉庫設(shè)備中讀出的數(shù)據(jù)。另外,在讀請求I/O完成函數(shù)中,還要遞減一個計數(shù)器,當(dāng)該計數(shù)器遞減到0時,說明我們已經(jīng)全部處理了mt_home鏈表中的項,這時得到一個以mr_bio_home為首,每項中都指向一個bio結(jié)構(gòu)的鏈表。
  struct list_head mt_home; //BIO更新鏈表
  struct most_recent_blocks { //BIO更新表項
   struct bio *mrbio;
   struct list_head list;
  };


    3. 將mt_bi_home鏈表的數(shù)據(jù)塊都恢復(fù)到主機(jī)磁盤設(shè)備中。
    這個操作相對比較簡單,我們只需要在主機(jī)磁盤設(shè)備上執(zhí)行mt_bi_home鏈表的每一個bio請求項即可。當(dāng)然,我們要在這些請求項的I/O完成方法中做善后處理,即如果所有請求項都已經(jīng)執(zhí)行完畢,則釋放mt_home鏈表和mt_bi_home鏈表。  

分享到

多易

相關(guān)推薦