午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

深度解讀原子寫 – 運維那點事

 鳳凰苑兇真 2019-01-29

為大家介紹一下所謂的原子寫,Atomic Write。

一、從文件系統(tǒng)刪除文件說起

文件刪除操作過程比較復(fù)雜,如果簡化的來講,可以分為兩步:

1. 刪除該文件在文件記錄表中的條目。

2. 將該文件之前所占據(jù)的空間對應(yīng)的塊在空間追蹤bitmap中將對應(yīng)的bit置0。

假設(shè)該文件的文件名非常短,尺寸也非常小,只有不到4KB,那么,上述這兩個動作,就可以分別只對應(yīng)一個4K的IO(如果文件系統(tǒng)格式化時選擇4K的分塊大小的話),第一個4K將更新后的記錄表覆蓋到硬盤對應(yīng)的區(qū)域,第二個IO將更新后的bitmap的這4K部分覆蓋下去。僅當(dāng)這兩個IO都結(jié)束時,該文件才會徹底被刪除。

該是問“如果”和“為什么”的時候了。如果,文件系統(tǒng)將更新記錄表這個IO發(fā)到了硬盤上并且成功寫入,而更新bitmap的IO沒有發(fā)出、或者發(fā)出了但是正在去往硬盤的路上的某處,此時系統(tǒng)突然斷電,那會有什么結(jié)果?

放在早期的文件系統(tǒng),再次重啟系統(tǒng)之后,會進(jìn)入FSCK(文件系統(tǒng)一致性檢查及修復(fù))階段,也就是WinXP那個經(jīng)典的藍(lán)底黃滾動條界面。因為文件系統(tǒng)會維護(hù)一個dirty/clean位,在做任何變更操作之后,只要操作完成,該位就被置為clean,那么下次重啟就不會進(jìn)入FSCK過程,而我們上述的例子中,這兩筆IO是一組不可分割的“事務(wù)(Transaction)”,一筆事務(wù)中的所有IO要么都被執(zhí)行,要么干脆別被執(zhí)行,結(jié)果就是這文件要么完全被刪除,要么就不被刪除還在那,大不了再刪除一次。但是,如果一筆事務(wù)中的某個/些IO完成,另一些沒完成,比如,記錄表中已經(jīng)看不到這個文件,但是空間占用追蹤bitmap中卻還記錄著該文件之前被占用的空間的話,那么表象上就會看到這樣的情況:雙擊我的電腦進(jìn)去某個目錄,看不到對應(yīng)的文件,而右鍵點擊硬盤屬性,卻發(fā)現(xiàn)該文件占用的空間并沒有被清掉。這就產(chǎn)生了不一致。所以,F(xiàn)SCK此時需要介入,重新掃描全部的記錄表,與bitmap中每個塊占用與否重新匹配,最后便會將bitmap中應(yīng)該被回收卻沒有來得及回收的bit重新回收回來。

所謂原子寫,就是指一筆不可分隔開的事務(wù)中的所有寫IO必須一起結(jié)束或者一起回退,就像原子作為化學(xué)變化中不可分割的最小單位一樣。

二、單筆寫IO會不會被原子寫?

上面的場景指出,一筆事務(wù)中的多筆IO可能不會被原子寫,那么單筆IO總能被原子寫了吧?很不幸,也無法被原子寫。原因和場景有下面三個:

2.1 上層一筆IO被分解成多筆IO

上層發(fā)出的一筆IO可能會被下層模塊分解為多筆IO,這多筆IO執(zhí)行之間如果斷電,無法保證原子性。有多種情況可以導(dǎo)致一筆IO被分解,比如:

A.   IO size大于底層設(shè)備或者IO通道控制器可接受的最大IO size時,此時會由Device Driver將IO分解之后再發(fā)送給Host Driver。

B.   做了Raid,條帶深度小于該IO的size,那么raid層會將該IO分解成多個IO。

2.2 外部IO控制器不會主動原子寫

那么,當(dāng)一筆IO(分解之后的或者未分解的,無所謂)請求到了底層,由Host Driver發(fā)送給外部IO控制器硬件的時候,外部IO控制器總可以實現(xiàn)原子寫了吧?IO控制器硬件總不可能只把這筆IO的一部分發(fā)給硬盤執(zhí)行吧?很不幸,IO控制器的確就是這樣做的。比如,假設(shè)某筆寫IO為32KB大小,IO控制器并不是從主存將這32KB數(shù)據(jù)都取到控制器內(nèi)緩沖區(qū)才開始向后端硬盤發(fā)起IO,而是根據(jù)后端SAS鏈路控制器前端的buffer空閑情況,來決定從Host主存DMA多少數(shù)據(jù)進(jìn)去,數(shù)據(jù)一旦進(jìn)入該buffer,那么后端SAS鏈路控制器就會將其封裝為SAS幀寫到后端硬盤上。這個buffer一般只有幾KB大小。所以很有可能一筆主機端的32KB的IO,在斷電之前,有部分已經(jīng)寫入硬盤了,而剩余的部分則未被寫入。雖然主機端的協(xié)議棧、應(yīng)用都沒有收到這筆IO的完成應(yīng)答,但是硬盤上的數(shù)據(jù)已經(jīng)被撕裂了,一半是舊的,一半是新的。

(Adaptec的Raid控制器一般會將整個IO取回到板載DDR RAM,然后將對應(yīng)的RAM pages設(shè)為dirty,然后返回給host寫應(yīng)答(向competition queue中入隊一個io完成描述結(jié)構(gòu)體)。也就是說,Adaptec的Raid卡是可以保證單IO原子寫的,但有個前提是Cache未滿,當(dāng)Cache滿或者某種原因被disable比如電容故障等的時候,就無法實現(xiàn)原子寫了。至于其他的卡是否保證,冬瓜哥并不清楚。)

2.3 硬盤也不會主動原子寫

硬盤本身并不會原子寫。硬盤接收到的數(shù)據(jù)也是一份一份的,每個SAS幀是1KB的Payload,SAS HBA會分多次將一筆IO發(fā)送給硬盤。至于硬盤是否會將這筆IO的所有數(shù)據(jù)都接收到才往盤片上寫入,冬瓜哥不是硬盤廠商的研發(fā)所以并不知曉,但是冬瓜哥知道的是,不管硬盤是攢足了再寫還是收到一個分片就寫,其內(nèi)部的磁頭控制電路前端一定也是有一定buffer的,該buffer被充滿就寫一次。不管怎么樣,當(dāng)磁頭在盤片上劃過將數(shù)據(jù)寫入盤片期間,突然斷電之后,盤片上的數(shù)據(jù)幾乎一定是一部分新一部分舊的,不一致,甚至一個扇區(qū)內(nèi)部都有可能被撕裂。縱使Host端的確會認(rèn)為該IO未完成,但是木已成半舟。

Every Enterprise SCSI drive provides 64k powerfail write-atomicity. We depend upon it and can silently corrupt data without it.

對于PCIE接口的固態(tài)盤,情形也是一樣的。SSD從主存DMA時一般每次DMA 512Byte,也就是PCIE Payload的普遍尺寸。當(dāng)攢足了一個Page的數(shù)據(jù)時,SSD就開始寫入Flash了,而并不是等整個IO數(shù)據(jù)全部DMA過來才寫入Flash。但是僅當(dāng)整個IO都寫入完畢之后,才會向host端competition queue寫入io完成描述結(jié)構(gòu)。如果是打開了write back模式的寫緩存,那么僅當(dāng)整個io數(shù)據(jù)全部DMA到寫緩存中之后才會返回io完成描述ack,但是掉電之后,不管是完整取回的還是部分取回的,未完成的io會不會由固態(tài)盤固件繼續(xù)完成,就取決于固件的實現(xiàn)了。

三、IO未完成,再來一遍不就行了么?

有人說了,既然Host端知道某筆IO未完成,那么重啟之后,對應(yīng)的應(yīng)用完全可以再重新發(fā)送這筆IO吧,重新把之前寫了一部分的數(shù)據(jù)全部再寫一遍不就行了么?這個問題很復(fù)雜,要分很多場景。

比如,Host未宕機,而是存儲系統(tǒng)突然宕機,或者突然承載存儲IO的網(wǎng)線斷掉。此時應(yīng)用程序會收到IO錯誤,取決于應(yīng)用程序如何處理,結(jié)果可能不同。比如應(yīng)用程序?qū)涌赡軙4嬗芯彌_,在這里實現(xiàn)原子寫,比如應(yīng)用可以在GUI彈出一個重試按鈕,當(dāng)外部IO系統(tǒng)恢復(fù)之后,用戶點擊重試之后,應(yīng)用會將該原子Transaction涉及的所有IO再次重新執(zhí)行一遍,此時便可以覆蓋之前不一致的數(shù)據(jù)為一致的。而如果外部存儲系統(tǒng)長時間不能恢復(fù),而應(yīng)用程序也被重啟或者強行關(guān)閉的話,那么該Transaction未完成,而且在硬盤上留下不一致的數(shù)據(jù)。當(dāng)應(yīng)用再次啟動的時候,取決于應(yīng)用處理方式的不同,結(jié)果也不同。

比如應(yīng)用完全依靠其操作員來決定該如何處理,比如如果是數(shù)據(jù)庫錄入,錄入員上一筆錄入失敗,那么其勢必再次錄入,此時應(yīng)用可以將錄入員再次錄入的數(shù)據(jù)覆蓋之前不一致的數(shù)據(jù)。但是更多實際場景未必如此,比如,錄入員可能并不是根本不管其要錄入的記錄之前是什么而直接錄入新數(shù)據(jù),而是必須參考之前的數(shù)據(jù)來決定新數(shù)據(jù),而之前的數(shù)據(jù)已經(jīng)不完整,或者錄入員并不知道該數(shù)據(jù)是錯誤的,而在錯誤數(shù)據(jù)的基礎(chǔ)上計算出了更加錯誤的新數(shù)據(jù),從而將更加錯誤的數(shù)據(jù)更新到硬盤上,埋了一顆雷,這就是所謂數(shù)據(jù)的”連環(huán)污染“。

再比如數(shù)據(jù)庫類的程序,其雖然記錄了redo log用于追蹤所有的變更操作,但是一旦某個數(shù)據(jù)塊發(fā)生不一致,redo log是無能為力的。如下圖所示的場景:

深度解讀原子寫

可以看到,1時刻內(nèi)存中的該數(shù)據(jù)塊,其CRC與數(shù)據(jù)是匹配的,而掉電后硬盤上的狀態(tài),CRC與整個數(shù)據(jù)塊是不匹配的。數(shù)據(jù)庫之類對數(shù)據(jù)一致性要求非常高的程序都會對每個數(shù)據(jù)塊做校驗以防止數(shù)據(jù)位由于各種原因發(fā)生bit躍變。但是對于上圖最右邊的情況,數(shù)據(jù)庫程序是無法判斷該塊到底是發(fā)生了bit翻轉(zhuǎn),還是由于底層沒有原子寫而導(dǎo)致的CRC不匹配,校驗錯誤,所以會認(rèn)為該塊是一個壞塊。當(dāng)然,本例中,我們預(yù)先知道該壞塊其實是由于原子寫失敗而導(dǎo)致的,但是程序并不知道。其實,此時用redo log強行把4321再覆蓋到第一行上,就可以恢復(fù)數(shù)據(jù),但是數(shù)據(jù)庫并不敢去這么做,已經(jīng)說了,數(shù)據(jù)庫并不知道該塊是不是由于比如第二行或者第三行里某些數(shù)據(jù)位發(fā)生反轉(zhuǎn)而導(dǎo)致的CRC校驗錯誤,所以不能直接把4321再寫一遍到第一行上就認(rèn)為該塊被恢復(fù)了,為了驗證該壞塊是否是由于4321未被原子寫入所導(dǎo)致,數(shù)據(jù)庫可以先讀入該塊到內(nèi)存,然后根據(jù)redo log把第一行改為4321,然后再算一遍CRC如果與壞塊中的CRC一致了,證明該壞塊的確是由于4321被撕裂而導(dǎo)致CRC不一致,此時數(shù)據(jù)庫可以把CRC更正過來然后恢復(fù)該塊。但是,如果是下面這種場景,數(shù)據(jù)庫就無能為力了:

深度解讀原子寫

該例中,第一行被完整更新,但是CRC未被完整更新,導(dǎo)致撕裂。數(shù)據(jù)庫發(fā)現(xiàn)redo log中的4321已經(jīng)被完整更新到了數(shù)據(jù)塊上,但是CRC依然錯誤,那么此時數(shù)據(jù)庫無法判斷到底是因為數(shù)據(jù)塊中其他的數(shù)據(jù)位發(fā)生了翻轉(zhuǎn)出錯,還是因為CRC未被原子寫,此時數(shù)據(jù)庫無計可施,只能報告壞塊。

四、業(yè)界為了避免數(shù)據(jù)不一致而做的妥協(xié)——兩次/三次提交

4.1 文件系統(tǒng)日志

如果將要寫入硬盤數(shù)據(jù)文件中的數(shù)據(jù)/元數(shù)據(jù)先不往原始文件中寫,而是寫到硬盤中的一個單獨的文件中,這個文件被稱為journal/日志(也有人叫l(wèi)og),F(xiàn)S收到下游協(xié)議棧返回成功信號之后才向應(yīng)用返回寫入成功信號。(這里又牽扯到如果設(shè)備寫成功了,而FS在向app返回成功信號時斷電,那么app認(rèn)為沒成功,而底層其實已經(jīng)成功了,此時就需要靠app來決定下一步動作,比如重新再來一遍,或者后續(xù)發(fā)現(xiàn)其實已經(jīng)成功了)。這個過程中,原始數(shù)據(jù)文件是沒被寫入的,依然保持上一個一致的狀態(tài)。日志中的數(shù)據(jù)/元數(shù)據(jù)在某些時候會寫入到硬盤上的原始數(shù)據(jù)/元數(shù)據(jù)文件中,比如每隔幾秒鐘會觸發(fā)一次針對原始數(shù)據(jù)/元數(shù)據(jù)文件的寫入,這個過程叫做checkpoint或者commit,如果這期間發(fā)生掉電宕機,重啟之后FS可以分析日志,將日志中沒有commit完成的操作再commit一遍到數(shù)據(jù)文件中。

有人會有疑問:如果FS在寫日志的時候,發(fā)生了宕機掉電導(dǎo)致的數(shù)據(jù)塊撕裂怎么辦?那么發(fā)生撕裂的這筆IO就處于未完成狀態(tài),此時該IO的數(shù)據(jù)會被完全丟棄,以原始數(shù)據(jù)文件中對應(yīng)的數(shù)據(jù)為準(zhǔn),也就是應(yīng)用再發(fā)起針對該IO目標(biāo)地址的讀操作時,F(xiàn)S會從原始數(shù)據(jù)文件中讀出內(nèi)容,此時讀出的便是上一次的一致狀態(tài)的數(shù)據(jù),此時應(yīng)用可以基于這個數(shù)據(jù)繼續(xù)工作,比如選擇重新錄入新數(shù)據(jù)。這就是兩次提交的好處,第一次先提交到日志文件,一旦IO被撕裂,那么原始數(shù)據(jù)文件中的數(shù)據(jù)依然是完好的。那么,如果數(shù)據(jù)被commit到原始數(shù)據(jù)文件的過程中,如果一旦發(fā)生數(shù)據(jù)塊撕裂,怎么辦?這個可以參考上述那兩個圖中所示的場景,F(xiàn)S只要重放(replay、redo)日志中未完成的操作,重新覆蓋一邊對應(yīng)的數(shù)據(jù)塊即可,由于多數(shù)FS并不對數(shù)據(jù)塊做校驗,所以不會出現(xiàn)上述那個問題。

4.2 MySQL Double Write Buffer

為了解決帶校驗的數(shù)據(jù)塊撕裂導(dǎo)致的壞塊誤判問題,MySQL采用了三次提交的方式。第一次先將IO寫操作數(shù)據(jù)提交到redo log日志中(如果IO尚未寫入log或者寫了一部分尚未commit之前宕機,那么重啟之后根據(jù)這個斷點undo回上一個commit點時的數(shù)據(jù)),第二次將本應(yīng)commit到原始數(shù)據(jù)文件中的數(shù)據(jù)再寫入到硬盤上一個單獨的文件中,叫做Double Write Buffer,當(dāng)commit到DWB的期間一旦發(fā)生掉電宕機,那么DWB里的數(shù)據(jù)就是不一致的,那么重啟之后,數(shù)據(jù)庫可以利用redo log+原始數(shù)據(jù)文件(一定是一致的),來重放/redo,從而將系統(tǒng)恢復(fù)到最近的時刻,重放期間依然是先從log寫入DWB,再從DWB寫入到數(shù)據(jù)文件(因為如果繞過DWB,直接從redo重放到原始數(shù)據(jù)文件的話,一旦該過程再宕機,原始數(shù)據(jù)文件就可能不一致,最后的希望也就沒了),如果重啟之后又宕機了,就再來一遍,循環(huán)。數(shù)據(jù)文件在被提交到DWB之后,就相當(dāng)于有了一份備份,數(shù)據(jù)庫再從這個備份中將數(shù)據(jù)導(dǎo)入到原始數(shù)據(jù)文件,如果導(dǎo)入過程中出現(xiàn)宕機,沒關(guān)系,重啟后只需要再從DWB的斷點重新再次覆蓋一下原始文件即可。

ext4文件系統(tǒng)的日志方式中有一個是data=jurnal,其底層就是先將數(shù)據(jù)和元數(shù)據(jù)更新都寫到日志中,然后再提交到原始數(shù)據(jù)文件中,這種機制相當(dāng)于MySQL的DWB了。

4.3 某傳統(tǒng)廠商文件系統(tǒng)的做法

該文件系統(tǒng)也采用日志技術(shù),但是為了保證速度,采用帶電池保護(hù)的RAM來承載日志。在向數(shù)據(jù)文件commit數(shù)據(jù)的時候,也采用類似MySQL DWB類似思想,但是形式卻不同。其每次都會將更新的數(shù)據(jù)寫入到硬盤上空閑的空間上,并且同時更改映射表指針的指向。每隔10秒鐘,或者系統(tǒng)內(nèi)其他一些功能所觸發(fā),該文件系統(tǒng)對數(shù)據(jù)文件批量提交更新的數(shù)據(jù),只不過,該過程并不是把數(shù)據(jù)拷貝到原始數(shù)據(jù)文件覆蓋,而是把元數(shù)據(jù)提交,由于元數(shù)據(jù)的更改每次也都是寫入空閑空間,所以元數(shù)據(jù)的提交無非就是最終將根指針的指向做一次跳轉(zhuǎn)而已。一旦在這個過程中任何一處發(fā)生宕機,那么重啟之后其可以利用日志重放之前的變更。由于其對每個數(shù)據(jù)塊也做了checksum校驗,所以其也會存在塊撕裂導(dǎo)致的壞塊問題。與DWB機制一樣,其每次將數(shù)據(jù)寫入空閑空間,上一次commit之后的數(shù)據(jù)文件并沒有被更改,所以一旦遇到壞塊,那么其可以利用日志+上一次的原始數(shù)據(jù)文件來進(jìn)行重放,如果在這個過程中又出現(xiàn)問題,或者各種bug或者未知原因?qū)е碌闹胤攀?,那么至少上一次成功commit之后的數(shù)據(jù)是可用的,其可以回滾到上一個狀態(tài),雖然有丟數(shù)據(jù),但是至少可以保證數(shù)據(jù)一致。

據(jù)不可靠消息,Oracle并沒有像MySQL一樣對數(shù)據(jù)塊采用三次提交的辦法,而是數(shù)據(jù)直接由內(nèi)存持續(xù)的寫入硬盤中的數(shù)據(jù)文件,此時,存在一定幾率由底層無法原子寫而導(dǎo)致的塊撕裂而無法使用redo log恢復(fù),從而出現(xiàn)各種級別的錯誤,嚴(yán)重者甚至整個庫無法被拉起來。

五、如何在外部存儲設(shè)備上實現(xiàn)原子寫

如果能夠在外部設(shè)備中保證IO的原子寫,那么諸如MySQL的DWB就可以不要了,會節(jié)省開銷提升IO性能。如果在硬盤中可以實現(xiàn)多IO為一組的原子寫,那么存儲系統(tǒng)控制器里為了保證一致性而做的復(fù)雜機制就可以被簡化。單IO原子寫的前提是,操作系統(tǒng)內(nèi)核模塊比如塊層里的LVM、軟Raid等,不能把單筆寫IO分割成多筆,如果在這里分割了,外部設(shè)備的單IO原子寫就失去了意義。然而,根本就無法保證軟raid和LVM不分割,這完全取決于條帶或塊大小以及應(yīng)用下發(fā)的IO大小。另外,Device Driver會向系統(tǒng)上報一個對應(yīng)設(shè)備所能支持的最大IO size,如果應(yīng)用的IO size大于這個size,Device Driver也會將其分割,所以,應(yīng)用層必須預(yù)先得到這些參數(shù),然后加以配合來實現(xiàn)單IO原子寫。

5.1 SAS/SATA硬盤實現(xiàn)單IO原子寫

HBA場景:HBA處不適合負(fù)責(zé)原子寫,因為HBA控制器內(nèi)部要盡量簡單和高效。那么當(dāng)HBA將數(shù)據(jù)源源不斷的用SAS或者SATA鏈路一幀一幀的傳送給硬盤時,硬盤僅當(dāng)將一筆IO的所有數(shù)據(jù)都接收到其內(nèi)部緩沖之后,才發(fā)起寫盤操作。而僅僅如此的話也并不能保證原子寫,硬盤必須在內(nèi)部采取日志方式,將該IO先寫入硬盤上保留的日志區(qū),日志成功寫入后,再向HBA控制器發(fā)送cmd complement幀,同時才能開始向數(shù)據(jù)區(qū)寫入數(shù)據(jù),同時HBA再向host端的competition queue入隊io完成描述。一旦上述過程宕機,硬盤重啟后可以用日志redo。如果IO之前尚未完整的寫入日志,則硬盤實際數(shù)據(jù)區(qū)的內(nèi)容也依然完好,硬盤只需要從日志中刪除該筆IO的不完整數(shù)據(jù)即可,就當(dāng)該IO沒發(fā)生過。這就是實現(xiàn)原子寫的代價,一定是降低了性能。有一個要注意的地方是,采用了日志方式之后,要保證IO的時序一致性,比如有一筆IO已經(jīng)被成功commit到日志,那么后續(xù)如果收到針對有重疊的目標(biāo)地址的讀IO,硬盤要返回已經(jīng)提交的數(shù)據(jù)而不是從盤片上讀出數(shù)據(jù)返回,這會增加計算量。

Raid卡場景: Raid卡是個帶內(nèi)虛擬化設(shè)備,適合實現(xiàn)原子寫,由于其具有天然的優(yōu)勢——有超級電容的保護(hù)。Raid卡只需要保證將一筆IO數(shù)據(jù)完全DMA到其內(nèi)部的緩沖器中,并將對應(yīng)的緩沖器page置為dirty,之后,便可以向host端完成隊列入隊該io已完成的描述了。這便實現(xiàn)了原子寫。

5.2 PCIE接口的固態(tài)盤實現(xiàn)單IO原子寫

如果打開了wb模式的寫緩存,固態(tài)盤必須將一筆寫IO的全部數(shù)據(jù)都收到自己的緩沖內(nèi)部之后,然后即可向host端完成隊列入隊io完成應(yīng)答。如果沒有打開寫緩存,那么僅當(dāng)該筆IO全部內(nèi)容都被commit到flash之后,固態(tài)盤固件才會向主機應(yīng)答。一旦上述過程中發(fā)生宕機,那么固態(tài)盤此時有兩種方式來保證IO的原子性。

方式1:redo模式。當(dāng)打開wb模式緩存時,該IO完整數(shù)據(jù)如果已經(jīng)進(jìn)入了緩存并且應(yīng)答,那么固態(tài)盤要保證該IO在掉電之后,依靠電容將其數(shù)據(jù)寫入flash,也就是掉電后必須redo。如果沒有電容或者電容容量太低,那么就得將該IO先寫入flash的日志區(qū)之后,再向主機應(yīng)答,重啟后redo日志。

方式2:undo模式。如果不打開寫緩存,固態(tài)盤控制器從host端主存源源不斷的DMA數(shù)據(jù)到內(nèi)部的容量非常有限的緩沖區(qū),只要緩沖滿一個頁面,控制器就開始向后端flash寫入。那么,根據(jù)上文所述,會產(chǎn)生不一致。但是,此時可以利用SSD內(nèi)部機制的一個天然優(yōu)勢,那就是,Redirect on Write機制,提到這個機制,大家可以想到上文中介紹的那個每次都重定向?qū)懙奈募到y(tǒng)的機制,也是RoW。上一次的舊數(shù)據(jù)并不被原地覆蓋,所以可以完好的保留。所以,SSD可以不等整個IO的數(shù)據(jù)都到緩沖就可以先讓數(shù)據(jù)寫到Flash,寫成功之后,再更新地址映射表,正如那個特殊文件系統(tǒng)的做法一樣,一旦數(shù)據(jù)寫入過程中發(fā)生宕機,那么下次重啟,由于地址映射表尚未更新,所以該目標(biāo)地址自然就會被指向之前的舊數(shù)據(jù),達(dá)到了“要么全寫入要么不寫入”的原子寫效果。這種方式可以認(rèn)為是出錯即undo的方式,與數(shù)據(jù)庫里的undo機制不同,由于SSD內(nèi)部天然的RoW,不需要像數(shù)據(jù)庫一樣采用CoW方式將舊數(shù)據(jù)拷貝出來形成回滾段。

5.3 實現(xiàn)多IO為一組的原子寫

要實現(xiàn)多個IO要么一起全寫入要么一起都不寫入的效果,就必須增加對應(yīng)的軟件描述接口,以便讓上游程序告訴下游部件“哪幾個IO需要實現(xiàn)原子性”,必須這樣,別無他法。

方式1:帶外方式。比如增加一種通知機制,單獨描述給下游部件,比如“事務(wù)開始,后續(xù)發(fā)送的32個IO為一組原子IO。帶外方式的缺點在于,IO必須連續(xù),期間不能被亂入其他非該事務(wù)的IO,而優(yōu)點在于能夠節(jié)省開銷;

方式2:帶內(nèi)方式。帶內(nèi)方式則是將該信息直接嵌入到每個IO上,比如第一個IO中嵌入“事務(wù)開始,事務(wù)ID=1,共32個IO,此為第一個”,期間可以被亂入其他非事務(wù)性IO或者其他事務(wù)ID的IO。該事務(wù)的最后一個IO(第32個)中會攜帶“事務(wù)結(jié)束,ID=1,共32個IO,此為第32個”的信息。帶內(nèi)方式優(yōu)點在于靈活,可以亂入其他事務(wù)或者非事務(wù)的IO,以供底層更充分的重排優(yōu)化,缺點在于開銷大,每筆IO都需要攜帶對應(yīng)信息。

由于多IO原子寫無法透明實現(xiàn),需要修改應(yīng)用、內(nèi)核以及外部硬件固件,生態(tài)關(guān)系協(xié)調(diào)困難,認(rèn)知度低,場景少,基本上可以靠數(shù)據(jù)庫自身的日志機制解決,所以目前實際產(chǎn)品中只有極少數(shù)采用私有訪問協(xié)議的產(chǎn)品實現(xiàn)了,而實現(xiàn)方式是上面的哪一種冬瓜哥也不清楚。

然而,理想雖好,由于實現(xiàn)方式復(fù)雜,所以一般產(chǎn)品都選擇不支持原子寫。即便支持,也基本上是上述的出錯即undo的方式,這樣能降低實現(xiàn)復(fù)雜度,同時不增加IO的時延,因為IO流程與非原子寫狀態(tài)下是一樣的,采用wormhle數(shù)據(jù)傳輸方式,只是寫成功后才更新映射表,寫不成功沒關(guān)系,回滾到舊數(shù)據(jù)塊上。而緩存模式+redo模式下,會增加IO時延,因為必須等IO數(shù)據(jù)全部DMA到緩存中之后才對主機端應(yīng)答,相當(dāng)于stor-forwarding數(shù)據(jù)傳輸方式。

摘自冬瓜哥:【冬瓜哥論文】原子寫,什么鬼?!


如果您覺得本站對你有幫助,那么可以支付寶掃碼捐助以幫助本站更好地發(fā)展,在此謝過。
喜歡 (3)or分享 (0)

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多