本文通過具體的設計思路來加深對快照的理解,如有錯誤,歡迎指出
基於CoFW的設計
工作流程
文件系統初始數據如下圖
文件系統映射表保存着數據和保存地址的對應關系,當上層要更改A0時,會尋址到邏輯地址0進行接下來的操作
准備一個IO倉庫用來存放變化的IO塊
T0時刻,系統觸發快照,此時創建一份針對T1時刻的快照S0的地址映射表F0
T1時刻,上層應用將B0數據更新為B1
系統查看F0,發現沒有對應條目,執行CoFW
首先將邏輯地址2中的數據B0拷貝到IO倉庫地址0的地方,更新F0,意思表示邏輯地址2的數據保存在IO倉庫0位置上
然后上層將B1覆蓋寫入邏輯地址2,更新文件系統映射表
T2時刻,C0更新為C2,流程同上
此時系統再次觸發一份快照S1,映射表為F1
T3時刻,B1更新為B3
系統發現F1沒有針對邏輯地址2的對應條目,進行CoFW操作
F0表已經保存過當時時刻的數據了,所以無需更新
T4時刻,將A0更新為A4
系統發現F0,F1均沒有對應表項,首先將A0拷貝到IO倉庫,然后更新快照映射表
最后將A4寫入,更新文件映射表
此時IO倉庫容量已滿,后續如果再有數據更新需要擴容
T5時刻,B3更新為B5
系統發現F0,F1均更新過,無需進行CoFW
將B5更新,更新文件映射表
任何時刻,讀IO都能直接通過文件系統映射表來查詢數據,無需查詢快照映射表
客戶端掛載快照
T6時刻,客戶端掛載快照S0
T8時刻,客戶端請求邏輯地址0的數據,F0表中查詢到對應表項,從IO倉庫3讀出A0返回
T9時刻,客戶端請求邏輯地址5的數據,F0表未查詢到數據,從源表查詢中找到邏輯地址5讀出D0返回
PS:查詢線程在查找映射表之前,必須先想CoFW線程查詢當前時刻是否存在針對源物理卷的寫IO,如果有,IO地址是否恰好為查詢的地址,如果是,則查詢線程要等待CoFW完成再去查詢映射表,此時就可以查到最新數據了。
如果查詢線程結果是從源物理卷讀取內容,則在查詢線程讀取完成之前,CoFW不能針對這個地址操作,否則查詢線程可能讀取的是最新剛被寫入的數據而不是對應以前快照時刻的數據了。
對於客戶端來說,它掛載S0后文件系統看到的內容如下:
T10時刻,客戶端更新A0為A10
因為客戶端不能影響原系統,所以此時需要對新IO單獨放在另一個地方,數據可以放入IO倉庫或其它設計,增加RoFW表來記錄寫IO
首先將A10寫入IO倉庫,更新客戶端文件系統映射表和RoFW表
T11時刻,客戶端讀取A10,首先查看RoFW表,發現存在表項,從IO倉庫4中讀出A10
T12時刻,客戶端讀取B0,RoFW表未查到表項,查詢F0表項,從IO倉庫0讀出B0
T13時刻,客戶端讀取E0,RoFW和F0均未查到表項,從源表查詢得到從邏輯地址7讀出E0
上述過程同樣考慮線程依賴關系
rollback
T14時刻,客戶端要求將快照S0回滾覆蓋源物理卷。
在rollback之前主機客戶端一定要把buffer里的數據flush到源卷,不然源卷被恢復之后,緩存里的數據再寫下去,數據就不一致了。所以一般在rollback之前,索性先umount掉,rollback完成再mount回來
這里可以前台或后台操作,前台操作即等回滾完畢之后才能進行IO操作,為了保證友好性,設計為后台操作,即直接返回成功。
因為主機認為成功了,可能就會繼續有讀寫IO。
T15時刻,系統開始執行與掛載快照讀寫時相同的操作,但是在后台,系統會將原先CoFW的數據以及被RoFW的數據覆蓋到源物理卷對應地址上。
在處理客戶端寫IO請求時也可以直接將覆蓋源卷對應地址,無需RoFW,節約一輪IO操作,這時要記錄每個在Rollback開始與完成之間發生的寫IO地址以便Rollback時跳過這些地址。
同時,線程依賴關系必須考慮。
T15時刻,所有條目都被恢復之后,F0表和RoFW表被刪掉,此刻之后,就相當於S0快照沒被創建過,系統只剩下S1快照
兩個注意點:
- 比如S0快照恢復時,要覆蓋S1的基准塊如C2,如果此時要保存S2快照,那么就需要先把C2拷貝到IO倉庫然后更新F1表之后再恢復C0
即在做覆蓋操作之前,必須參考恢復快照點之后所有快照點的映射表,一旦任何一份表中對應的地址沒有映射條目,就要CoFW操作
然而,目前市面上的產品幾乎都嚴格遵照歷史規則,即,既然選擇回到了S0,那么S0之后所發生的所有事件就不應該存在,所以S1也要被刪除
- rollback沒有完成之前,有新的快照建立,新快照的CoFW操作過程和后台rollback操作沖突,除非引入額外的CoFW操作才能保持數據一致性
刪除
T14時刻, 客戶端卸載S0快照,系統接收到請求后,刪除RoFW映射表,其他不做更改。如果再次掛載,則產生新的干凈的映射表
如果客戶希望卸載虛擬卷之后系統仍然保持所做的更改,則需要通知系統創建一份針對S0的克隆卷,所謂的clone卷也不過只是永久保存RoFW映射表而已。
T15時刻,系統請求刪除快照S0,此時掃描所有快照映射表,找出只有F0存在而其余映射表不存在的表項,將對應IO倉庫對應位置置為空閑。
查詢完畢發現IO倉庫0和1位置是只給S0使用的,置為空閑,然后刪除表F0。這個過程依然涉及線程依賴。
基於RoFW的設計
工作流程
文件系統初始數據如下圖
文件系統映射表保存着數據和保存地址的對應關系,當上層要更改A0時,會尋址到邏輯地址0進行接下來的操作
准備一個IO倉庫用來存放變化的IO塊
T0時刻,觸發快照S0,創建對應的映射表F0
T1時刻,寫入A1
系統直接將A1寫入IO倉庫,更新F0,表示邏輯地址0的數據被重定向到IO倉庫位置0處了
源卷自身永遠都是S0時刻的影像,永久凍結,拒絕寫入,除非所有快照都被刪除
T2時刻,寫入C2,流程同上
寫完之后觸發快照S1
T3時刻,寫入A3
此時源卷,F0以及F0對應IO倉庫的數據共同構成了S1快照的基准卷,這三者共同體也將會被永久凍結
IO倉庫寫入A3后,更新F1
T4時刻,寫入A4
此時,系統檢查最晚的映射表即F1是否有對應條目,如果有,則將A4在IO倉庫覆蓋A3
映射表不做任何更改
T5時刻,B5寫入
系統檢查F1中沒有對應的條目,將B5寫入IO倉庫空閑位置,更新F1
T6時刻,系統要讀取邏輯地址0和5的數據
系統先檢查最后一張表即F1,發現了邏輯地址0對應的條目,從IO倉庫2位置讀取A4
F1沒有找到邏輯地址5的條目,尋找上一張表,依舊沒有,於是從源卷邏輯地址5讀出D0
T7時刻,觸發快照S2,創建F2,此時F0,F1均被凍結
寫入A7,D7
F2中均沒有對應條目,將數據寫入IO倉庫並更新F2
客戶端掛載快照
T8時刻,客戶端掛載S0快照
S0其實就是當前物理源卷,客戶端發起的讀請求直接對應到源卷,不牽扯任何映射運算
T9時刻,客戶端掛載快照S1
客戶端讀取內容時先去查詢F0,沒有找到條目就會從源上讀取
比如讀取邏輯地址0,會從IO倉庫中讀取A1;讀取邏輯地址2,會從源卷讀取B0
這里要搞清楚的是本次快照影響實際是有源+上一次快照共同凍結而成,而最后一份快照映射表指針表示源最新數據狀態,而不是這份快照時刻的狀態
PS:RoFW設計模式不像CoFW模式存在線程依賴關系,對快照虛擬卷的讀操作不會對源卷的寫操作有任何的牽連和影響
T10時刻,系統受到針對S2快照的寫操作,與CoFW模式相同,這里也是建立一個針對S2的RoFW數據映射表,后續讀數據也是先查這個表
rollback
T11時刻,客戶端要求將S0回滾
由於S0快照點就是當前源物理卷,所以不需要任何額外操作,直接就完成了
T11時刻,客戶要求將S1回滾,這里不保留任何其它的快照
系統首先將F1之前的映射表做合並,如果在多個表有相同條目,則保留最晚的表條目
表合並完畢后,將IO倉庫對應的數據覆蓋到源卷,處理完畢后,IO倉庫清空,rollback完成
這個過程中遇到寫IO處理流程同CoFW
刪除
T11時刻,刪除快照S0
比較F0和后面的映射表,只在F0中有的條目,將數據拷貝回源卷,並將IO倉庫對應位置置為空閑
也可以將只在F0中存在的表項移到F1中