1. 程式人生 > >Redis持久化方式RDB與AOF詳解

Redis持久化方式RDB與AOF詳解

redis持久化 rdb aof 優勢 管理參數

前言


Redis提供了兩種數據存儲方式,分別是:cache-only && persistence;cache-only顧名知義,是用與緩存服務的,數據在服務器終止後將消失,在此模式下將不存在"數據恢復"的方式,是一種安全性低、效率高、易擴展的模式;persistence即內存中的數據持久備份至磁盤文件中,在服務重啟之後能夠恢復數據,這種模式下數據的安全性大大提高。cache-only沒有什麽講的,這裏主要說明Redis的持久化存儲模式。對於persistence持久化存儲,Redis提供了兩種持久化方法"Redis DataBase"簡稱"RDB",以及"Append-Only Flie"簡稱"AOF"

,當然了除了這兩種方式之外,Redis在早期版本中還使用虛擬內存的方法,不過在Redis 3.0版本之後已然被廢棄掉了。

RDB持久化方式


RDB是"Redis DataBase"的簡稱,RDB持久化是指按指定的策略將內存中的數據集快照並寫入磁盤。是默認的持久化方式,這種方式就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名為dump.rdb。可以通過修改配置文件設置自動做快照的方式。我們可以設置redis在n秒內如果超過m次key被修改就自動做快照,下面是默認的快照保存配置。匹配優先級由下到上。

save 900 1 # 900秒內如果超過1次key被修改,則發起快照保存

save 300 10 # 300秒內超過10次key被修改,則發起快照保存

save 60 10000 # 60s內發生10000次可以值修改,則發起快照

1 RDB文件存儲過程

1)當有相關操作時,redis父進程調用fork(),創建子進程。

2)父進程繼續處理client請求,子進程負責將內存內容寫入到臨時文件。由於os的寫時復制機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會為父進程要修改的頁面創建副本,而不是寫共享的頁面。所以子進程的地址空間中的數 據是fork()時刻整個數據庫的一個快照。

3)當子進程將快照寫入臨時文件完畢後,用臨時文件替換原有的快照文件,然後子進程退出。

Redis的client也可以使用"save"或者"bgsave"命令通知redis做一次快照持久化。save操作是在主線程中保存快照的,由於redis是用一個主線程(即單進程)來處理所有client的請求,這種方式將會阻塞所有client請求,不推薦使用。

值得註意的是,每次RDB都是將內存數據完整寫入到磁盤一次,並不是增量的只同步臟數據。如果數據量大的話,而且寫操作比較多,必然會引起大量的磁盤IO操作,可能會嚴重影響性能。

2 RDB的優勢

1)一旦采用該方式,那麽你的整個Redis數據庫將只包含一個文件,這樣非常方便進行備份。比如你可以每1天備份一次數據,而不用擔心磁盤空間不夠。而且我們可以很容易的將一個RDB文件移動到其他的存儲介質上。

2)RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。

3)RDB 可以最大化Redis的性能:父進程在保存RDB文件時唯一要做的就是調用fork()生成子進程,然後這個子進程就會處理接下來的所有保存工作,而父進程無需參與磁盤IO,大大提升性能。

3 RDB的劣勢

1)如果服務器運行時發生故障,那麽RDB方式將會丟失數據,雖然Redis允許你設置不同的保存點(save point)來控制保存RDB文件的頻率, 但是, 因為RDB 文件需要保存整個數據集的狀態,所以它並不是一個輕松的操作。 因此你可能會至少 5 分鐘才保存一次RDB文件。 在這種情況下, 一旦發生故障停機, 你就可能會丟失好備份時間段中的數據。

2)每次保存 RDB 的時候,Redis都要fork()出一個子進程,並由子進程來進行實際的持久化工作。在數據集比較龐大時,fork() 可能會非常耗時,造成服務器在某毫秒內停止處理客戶端; 如果數據集非常巨大,並且 CPU 時間非常緊張的話,那麽這種停止時間甚至可能會長達整整一秒。 雖然AOF重寫也需要進行fork() ,但無論AOF重寫的執行間隔有多長,數據的耐久性都不會有任何損失。

4 RDB的相關配置參數

除了上面用於定義RDB快照條件的項之外,還有一些其他的配置參數,放置在配置文件"/etc/redis.conf"中的"SNAPSHOTTING"字段中;詳細信息如下:

rdbcompression yes # 是否開啟壓縮功能

rdbchecksum yes # 檢測數據校驗和

dbfilename dump.rdb # 數據集文件名

dir /var/lib/redis # 數據存放目錄

stop-writes-on-bgsave-error yes # 默認情況下,如果啟用了RDB快照(至少有一個保存點),而最新的數據保存失敗,將停止接受寫入。這將使用戶(以一種困難的方式)意識到數據並不是正確地在磁盤上進行持續的,否則很可能沒有人會註意到,並且會發生一些錯誤,即使存在磁盤、權限等問題,仍然照常工作。如果後臺保存進程再次開始工作,Redis將自動允許再次寫入。 但是,如果您已經設置了對Redis服務器和持久性的正確監視,則應該禁用此功能,以便即使磁盤,權限等方面存在問題,Redis也將照常繼續工作。

AOF持久化方式


AOF是"Append-Only File"的簡稱,是一種將內存中的數據以命令的方式追加保存至臨時文件中,然後依賴次此文件進行數據重現的方式。

1 AOF文件保存過程

redis會將每一個收到的寫命令都通過write函數追加到文件中(默認是appendonly.aof)。當redis重啟時會通過重新執行文件中保存的寫命令在內存中重建整個數據庫的內容。當然由於os會在內核中緩存 write做的修改,所以可能不是立即寫到磁盤上。這樣AOF方式的持久化也還是有可能會丟失部分修改。不過我們可以通過選項告訴redis我們想要 通過fsync函數強制os寫入到磁盤。有三種方式如下(默認是:每秒fsync一次)

appendonly yes # 啟用aof持久化方式

appendfsync always # 每次收到寫命令就立即強制寫入磁盤,雖然保證完全的持久化,但是嚴重影響性能,不推薦使用。

appendfsync everysec # 每秒鐘強制寫入磁盤一次,在性能和持久化方面做了很好的折中,推薦。

appendfsync no # 完全依賴os,即Redis在不參與。這種性能最好,但持久化沒保證。

AOF的方式也同時帶來了另一個問題。持久化文件會變的越來越大。例如我們調用"incr count"命令100次,文件中必須保存全部的100條命令,其實前99條都是多余的。因為要恢復數據庫的狀態時只需要執行"set test 100"就夠了。為了壓縮aof的持久化文件。redis提供了"bgrewriteaof"命令。收到此命令redis將使用與快照類似的方式將內存中的數據 以命令的方式保存到臨時文件中(類似於Mysql中的二進制日誌),最後替換原來的文件。具體過程如下:

1)redis調用fork ,生成子進程

2)子進程根據內存中的數據庫快照,向臨時文件中寫入重建數據庫狀態的命令

3)父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進程重寫失敗的話並不會出問題。

4)當子進程把快照內容以命令的方式寫到臨時文件中後,子進程發信號通知父進程。然後父進程把緩存的寫命令也追加寫入到臨時文件中。

5)現在父進程可以使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。

值得註意的是:重寫aof文件的操作,並沒有讀取舊的aof文件,而是將整個內存中的數據庫內容以命令的方式重寫至了一個新的aof文件,這點和快照有點類似。

2 AOF持久化優勢

1)使用 AOF 持久化會讓 Redis 變得非常耐久(much more durable):你可以設置不同的 fsync 策略,比如無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。 AOF 的默認策略為每秒鐘 fsync 一次,在這種配置下,Redis 仍然可以保持良好的性能,並且就算發生故障停機,也最多只會丟失一秒鐘的數據( fsync 會在後臺線程執行,所以主線程可以繼續努力地處理命令請求)。

2)AOF文件是一個只進行追加操作的日誌文件(append only log), 因此對AOF文件的寫入不需要進行seek , 即使日誌因為某些原因而包含了未寫入完整的命令(比如寫入時磁盤已滿,寫入中途停機,等等), redis-check-aof 工具也可以輕易地修復這種問題。

Redis 可以在 AOF 文件體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操作是絕對安全的,因為 Redis 在創建新 AOF 文件的過程中,會繼續將命令追加到現有的 AOF 文件裏面,即使重寫過程中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件創建完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,並開始對新 AOF 文件進行追加操作。

3)AOF 文件有序地保存了對數據庫執行的所有寫入操作, 這些寫入操作以 Redis 協議的格式保存, 因此 AOF 文件的內容非常容易被人讀懂, 對文件進行分析(parse)也很輕松。 導出(export) AOF 文件也非常簡單: 舉個例子, 如果你不小心執行了FLUSHALL命令, 但只要 AOF 文件未被重寫,那麽只要停止服務器,移除AOF文件末尾的FLUSHALL命令,並重啟Redis ,就可以將數據集恢復到FLUSHALL執行之前的狀態。

3 AOF持久化的劣勢

1)對於相同的數據集來說,AOF 文件的體積通常要大於 RDB 文件的體積。

2)根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。在一般情況下,每秒 fsync 的性能依然非常高,而關閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間(latency)。

3)AOF在過去曾經發生過這樣的 bug : 因為個別命令的原因,導致 AOF 文件在重新載入時,無法將數據集恢復成保存時的原樣。(例如,阻塞命令 BRPOPLPUSH 就曾經引起過這樣的bug) 測試套件裏為這種情況添加了測試:它們會自動生成隨機的、復雜的數據集, 並通過重新載入這些數據來確保一切正常。雖然這種 bug 在 AOF 文件中並不常見,但是對比來說,RDB幾乎是不可能出現這種bug的。

4 AOF的相關配置參數

關於Redis的持久化方式之一"AOF",與其相關的配置項放置在Redis配置文件"/etc/redis.conf"中的"APPEND ONLY MODE"字段中;與其相關的參數如下:

# 開啟AOF持久化模式

appendonly no

# The name of the append only file (default: "appendonly.aof")

appendfilename "appendonly.aof"

# AOF持久化方式,只要分為三種,有寫操作就fsync(影響性能)、每秒fync(折中方式,只丟失1s數據)以及OS執行刷寫(持久化無保證),

# appendfsync always

appendfsync everysec

# appendfsync no

# 是否在後臺執行aof重寫期間不調用fsync,默認為no,表示調用;當AOF的fsync策略設置為always或everysec時,並且後臺保存進程(後臺保存或AOF日誌背景重寫)對磁盤執行大量I/O時,Redis可能在fsync()調用上阻塞太久。目前並沒有解決此問題的方法,因為即使在其他線程中執行fsync也會阻止我們的同步寫入調用。為了減輕這個問題,可以使用下面的選項來防止在BGSAVE或BGREWRITEAOF進程中在主進程中調用fsync()。這意味著,當另一個子進程正在保存時,Redis的持久性與"appendfsync none"相同。這意味著在最壞的情況下(使用默認的設置),最多可能會丟失30秒的日誌。 如果有延遲問題,請將其轉為"yes"

no-appendfsync-on-rewrite no

# 定義文件重寫頻度,有兩個參數,一是百分不,即系列第一條命令;二是按AOF日誌大小,默認為64MB。如果日誌當前的增漲量大於預定義的增長率(百分比)則觸發重寫。此外,您還需要指定要重寫的AOF文件的最小大小,即使達到了百分比增加但仍然非常小,這對於避免重寫AOF文件很有用。指定百分比為零,表示禁用自動AOF重寫功能。

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

# 在Redis啟動過程中,當AOF數據被加載回內存時,可能會發現一個AOF文件被截斷。如果將AOF-load-truncated設置為yes,則會加載一個截斷的AOF文件,而Redis服務器開始發出日誌,以通知該事件的用戶。如果該選項被設置為no,服務器將以錯誤中止並拒絕啟動。當選項設置為no時,用戶需要使用"redis-checkaof"實用程序修復AOF文件,然後再重新啟動服務器。

aof-load-truncated yes

總結:


一般來說, 如果想達到足以媲美 PostgreSQL 的數據安全性,你應該同時使用兩種持久化功能。如果你非常關心你的數據,但仍然可以承受數分鐘以內的數據丟失,那麽你可以只使用RDB持久化。


本文出自 “vinsent” 博客,請務必保留此出處http://vinsent.blog.51cto.com/13116656/1983267

Redis持久化方式RDB與AOF詳解