Python–Redis實戰:第四章:資料安全與效能保障:第3節:AOF持久化

Mark發表於2018-11-16

上一篇文章:Python–Redis實戰:第四章:資料安全與效能保障:第2節:快照持久化
下一篇文章:Python–Redis實戰:第四章:資料安全與效能保障:第4節:複製

AOF持久化

簡單來說,AOF持久化會將被執行的命令寫到AOF檔案的末尾,以此來記錄資料傳送的變化。因此,Redis只要從頭到尾重新執行一次AOF檔案包含的所有寫命令,就可以恢復AOF檔案所記錄的資料集。AOF持久哈可以通過設定程式碼appendonly yes配置選項來開啟。

檔案同步:在向硬碟寫入檔案時,至少會發生三件事情,當呼叫file.write()方法(或其它程式語言裡面的類似操作)對檔案進行寫入時,寫入的內容首先會被儲存到緩衝區,然後作業系統會在將來的某個時候將緩衝區儲存的內容寫入硬碟,而資料只有在被寫入硬碟後,才算是真正地儲存到了硬碟裡面。使用者可以通過呼叫file.flush()方法來請求作業系統儘快地將緩衝區儲存的資料寫入硬碟裡面,但具體地執行寫入操作仍然由系統決定。除此之外,使用者還可以命令作業系統將檔案同步(sync)到硬碟,同步操作會一直阻塞直到指定的檔案被寫入硬碟為止。當同步操作執行完畢之後,即使系統出現故障也不會低被同步的檔案造成任何影響。

下表展示了appendfsync 配置選項對AOF檔案的同步頻率的影響。

選項 同步頻率
always 每個Redis寫命令都要同步寫入硬碟。這樣做會嚴重降低Redis的速度。
everysec 每秒執行一次同步,顯示地將多個寫命令同步到硬碟。
no 讓作業系統來決定應該何時進行同步。

如果使用者使用appendfsync always 選項的話,那麼每個Redis寫命令都會被寫入硬碟,從而將發生系統崩潰時出現的資料丟失減到最少。不過遺憾的是,因為這種同步策略需要對硬碟進行大量寫入,所以Redis處理命令的速度會受到硬碟效能的限制:轉盤式硬碟(spinning disk)在這種同步頻率下每秒只能處理大約200個寫命令,而固態硬碟(solid-state drive,SSD)每秒大概也只能處理幾萬個寫命令。

警告:固態硬碟和appendfsync always

使用固態硬碟的使用者請謹慎使用appendfsync always選項,因為這個選項讓Redis每次只寫入一個命令,而不是像其他appendfsync選項那樣一次寫入多個命令,這種不斷地寫入少量資料的做法有可能會引發嚴重的寫入放大(write amplification)問題,在某些環境下甚至會將固態硬碟的壽命從原來的幾年降低為幾個月。

為了兼顧資料安全和寫入效能,使用者可以考慮使用appendfsync everysec選項,讓Redis以每秒一次的頻率對AOF檔案進行同步。Redis每秒同步一次AOF檔案時的效能和不使用任何持久化特性時的效能相差無幾,而通過每秒同步一次AOF檔案,Redis可以保證,即使出現系統崩潰,使用者也最多隻會丟失一秒之內產生的資料。當硬碟忙於執行寫入操作的時候,Redis還會優雅地放慢自己的速度以便適應硬碟的最大寫入速度。

最後,如果使用者使用appendfsync no選項,那麼Redis將不對AOF檔案執行任何顯示的同步操作,而是由作業系統來決定應該在何時對AOF檔案進行同步。這個選項在一般情況下不會對Redis的效能帶來影響,但系統崩潰將導致使用這種選項的Redis伺服器丟失不定數量的資料。另外,如果使用者的硬碟處理寫入操作的速度不夠快的話,那麼當緩衝區被等待寫入磁碟的資料填滿時,Redis的寫入操作將被阻塞,並導致Redis處理命令請求的速度變慢。因為這個原因,一般來說並不推薦使用appendfsync no選項,在這裡介紹了它只是為了完整列舉appendfsync選項的可用的3個值。

雖然AOF持久化非常靈活地提供了多種不同的選項來滿足不同應用程式對資料安全的不同要求,但AOF持久化也有缺陷:那就是AOF檔案的體積大小。

重寫、壓縮AOF檔案

AOF持久化既可以將丟失資料的時間區間降低至1秒(甚至不丟失任何資料),又可以在極端的時間內完成定期的持久化操作,那麼我們有什麼理由不使用AOF持久化呢?但是這個問題實際上並沒有那麼簡單,因為Redis會不斷地將被執行的寫命令記錄到AOF檔案裡面,所有隨著Redis不斷執行,AOF檔案的體積會不斷增長,在極端情況下,體積不斷增大的AOF檔案甚至可能會用完磁碟的所有可用空間。還有另一個問題是,因為Redis在重啟之後需要通過重新執行AOF檔案記錄的所有寫命令的還原資料集,所以如果AOF檔案的體積非常大,那麼還原操作執行的時間就可能會非誠長。

為了解決AOF檔案體積不斷增大的問題,使用者可以向Redis傳送bgrewriteaof命令,這個命令會通過移除AOF檔案中的冗餘命令來重寫(rewrite)AOF檔案,使AOF檔案的體積變得儘可能地小。bgrewriteaof的工作原理和bgsave建立快照的工作原因非常相似:Redis會建立一個子程式,然後由子程式負責對AOF檔案進行重寫。因為AOF檔案重寫也需要用到子程式,所以快照持久化因為建立子程式而導致的效能問題和記憶體佔用問題,在AOF持久化中也同樣存在。更糟糕的是,如果不加以控制的話,AOF檔案的體積可能會比快照恩簡的體積大好幾倍,在進行AOF重寫並刪除舊AOF檔案的時候,刪除一個體積達到數十GB大的舊AOF檔案可能會導致作業系統掛起數秒。

跟快照持久化可以通過設定save選項來自動執行bgsave一樣,AOF持久化也可以通過設定auto-aof-rewrite-percentage選項和auto-aof-rewrite-min-size選項來自動執行bgrewriteaof。舉個例子,假設使用者對Redis設定了配置選項auto-aof-rewrite-percentage 100和auto-aof-rewrite-min-size 64mb,並且啟用了AOF持久化,那麼當AOF檔案的體積大於64MB,並且AOF檔案的體積比上一次重寫之後的體積大了至少一倍(100%)的時候,Redis將執行bgrewriteaof命令。如果aof重寫執行得過於頻繁地話,那麼使用者可以考慮將auto-aof-rewrite-percentage選項的值設定為100以上,這種做法可以讓Redis在AOF檔案的體積變得更大之後才執行重寫操作,不過也會讓Redis在啟動時還原資料集所需的時間變得更長。

無論是使用AOF持久化還是快照持久化,將資料持久化到硬碟上都是非常有必要的,但除了進行持續化之外,使用者還必須對持久化所得的檔案進行備份(最好是備份到不同的地方),這樣才能儘量避免資料丟失事故發生。如果條件允許的話,最好能夠將快照檔案和最新重寫的AOF檔案備份到不同的伺服器上面。

通過使用AOF持久化或者快照持久化,使用者可以在系統重啟或者崩潰的情況下仍讓保留資料。隨著負載量的上升、或者資料的完整性變得越來越重要時,使用者可能需要使用複製性。

上一篇文章:Python–Redis實戰:第四章:資料安全與效能保障:第2節:快照持久化
下一篇文章:Python–Redis實戰:第四章:資料安全與效能保障:第4節:複製

相關文章