Redis持久化-RDB與AOF

asoren發表於2016-05-03

Redis持久化(Persistence)

Redis提供了不同持久化範圍的選項: 

RDB持久化以指定的時間間隔執行資料集的即時點(point-in-time)快照。
AOF持久化在服務端記錄每次收到的寫操作,在伺服器啟動時會重放,以重建原始資料集。命令使用和Redis協議一樣的格式以追加的方式來記錄。當檔案太大時Redis會在後臺重寫日誌。
如果你願意,你可以完全禁止持久化,如果你只是希望你的資料在伺服器執行期間才存在的話。
可以在同一個例項上同時支援AOF和RDB。注意,在這種情況下,當Redis重啟時,AOF檔案會被用於重建原始資料集,因為它被保證是最完整的資料。

理解RDB和AOF持久化之間的各自優劣(trade-offs)是一件非常重要的事情。讓我們先從RDB開始: 

RDB優點(RDB advantages) 

RDB是一種表示某個即時點的Redis資料的緊湊檔案。RDB檔案適合用於備份。例如,你可能想要每小時歸檔最近24小時的RDB檔案,每天儲存近30天的RDB快照。這允許你很容易的恢復不同版本的資料集以容災。
RDB非常適合於災難恢復,作為一個緊湊的單一檔案,可以被傳輸到遠端的資料中心,或者是Amazon S3(可能得加密)。
RDB最大化了Redis的效能,因為Redis父程式持久化時唯一需要做的是啟動(fork)一個子程式,由子程式完成所有剩餘工作。父程式例項不需要執行像磁碟IO這樣的操作。
RDB在重啟儲存了大資料集的例項時比AOF要快。

RDB缺點(RDB disadvantages) 
當你需要在Redis停止工作(例如停電)時最小化資料丟失,RDB可能不太好。你可以配置不同的儲存點(save point)來儲存RDB檔案(例如,至少5分鐘和對資料集100次寫之後,但是你可以有多個儲存點)。然而,你通常每隔5分鐘或更久建立一個RDB快照,所以一旦Redis因為任何原因沒有正確關閉而停止工作,你就得做好最近幾分鐘資料丟失的準備了。 
RDB需要經常呼叫fork()子程式來持久化到磁碟。如果資料集很大的話,fork()比較耗時,結果就是,當資料集非常大並且CPU效能不夠強大的話,Redis會停止服務客戶端幾毫秒甚至一秒。AOF也需要fork(),但是你可以調整多久頻率重寫日誌而不會有損(trade-off)永續性(durability)。 

AOF優點(AOF advantages) 

使用AOF Redis會更具有可永續性(durable):你可以有很多不同的fsync策略:沒有fsync,每秒fsync,每次請求時fsync。使用預設的每秒fsync策略,寫效能也仍然很不錯(fsync是由後臺執行緒完成的,主執行緒繼續努力地執行寫請求),即便你也就僅僅只損失一秒鐘的寫資料。
AOF日誌是一個追加檔案,所以不需要定位,在斷電時也沒有損壞問題。即使由於某種原因檔案末尾是一個寫到一半的命令(磁碟滿或者其他原因),redis-check-aof工具也可以很輕易的修復。
當AOF檔案變得很大時,Redis會自動在後臺進行重寫。重寫是絕對安全的,因為Redis繼續往舊的檔案中追加,使用建立當前資料集所需的最小操作集合來建立一個全新的檔案,一旦第二個檔案建立完畢,Redis就會切換這兩個檔案,並開始往新檔案追加。
AOF檔案裡面包含一個接一個的操作,以易於理解和解析的格式儲存。你也可以輕易的匯出一個AOF檔案。例如,即使你不小心錯誤地使用FLUSHALL命令清空一切,如果此時並沒有執行重寫,你仍然可以儲存你的資料集,你只要停止伺服器,刪除最後一條命令,然後重啟Redis就可以。

AOF缺點(AOF disadvantages) 

對同樣的資料集,AOF檔案通常要大於等價的RDB檔案。
AOF可能比RDB慢,這取決於準確的fsync策略。通常fsync設定為每秒一次的話效能仍然很高,如果關閉fsync,即使在很高的負載下也和RDB一樣的快。不過,即使在很大的寫負載情況下,RDB還是能提供能好的最大延遲保證。
在過去,我們經歷了一些針對特殊命令(例如,像BRPOPLPUSH這樣的阻塞命令)的罕見bug,導致在資料載入時無法恢復到儲存時的樣子。這些bug很罕見,我們也在測試套件中進行了測試,自動隨機創造複雜的資料集,然後載入它們以檢查一切是否正常,但是,這類bug幾乎不可能出現在RDB持久化中。為了說得更清楚一點:Redis AOF是通過遞增地更新一個已經存在的狀態,像MySQL或者MongoDB一樣,而RDB快照是一次又一次地從頭開始創造一切,概念上更健壯。但是,1)要注意Redis每次重寫AOF時都是以當前資料集中的真實資料從頭開始,相對於一直追加的AOF檔案(或者一次重寫讀取老的AOF檔案而不是讀記憶體中的資料)對bug的免疫力更強。2)我們還沒有收到一份使用者在真實世界中檢測到崩潰的報告。

我們該選誰(what) 
通常來說,你應該同時使用這兩種持久化方法,以達到和PostgreSQL提供的一樣的資料安全程度。 
如果你很關注你的資料,但是仍然可以接受災難時有幾分鐘的資料丟失,你可以只單獨使用RDB。 
有很多使用者單獨使用AOF,但是我們並不鼓勵這樣,因為時常進行RDB快照非常方便於資料庫備份,啟動速度也較之快,還避免了AOF引擎的bug。 
注意:基於這些原因,將來我們可能會統一AOF和RDB為一種單一的持久化模型(長遠計劃)。 
下面的部分將介紹兩種持久化模型等多的細節。

快照(Snapshotting)

預設情況下,Redis儲存資料集快照到磁碟,名為dump.rdb的二進位制檔案。你可以設定讓Redis在N秒內至少有M次資料集改動時儲存資料集,或者你也可以手動呼叫SAVE或者BGSAVE命令。 
例如,這個配置會讓Redis在每個60秒內至少有1000次鍵改動時自動轉儲資料集到磁碟: 

save 60 1000  

這種策略被稱為快照。 

如何工作(How works) 
每當Redis需要轉儲資料集到磁碟時,會發生: 

Redis呼叫fork()。於是我們有了父子兩個程式。
子程式開始將資料集寫入一個臨時RDB檔案。
當子程式完成了新RDB檔案,替換掉舊檔案。

這個方法可以讓Redis獲益於寫時複製(copy-on-write)機制。 
只追加檔案(Append-only file) 
快照並不是非常具有可永續性(durable)。如果你執行Redis的電腦停機了,電源線斷了,或者你不小心kill -9掉你的例項,最近寫入Redis的資料將會丟失。儘管這個對一些應用程式來說不是什麼大事,但是也有一些需要完全可永續性(durability)的場景,在這些場景下可能就不合適了。 
只追加檔案是一個替代方案,是Redis的完全可永續性策略。在1.1版本中就可用了。 
你可以在你的配置檔案中開啟AOF: 

Java程式碼 收藏程式碼
appendonly yes

從現在開始,每次Redis收到修改資料集的命令,將會被追加到AOF中。當你重啟Redis的時候,就會重放(re-play)AOF檔案來重建狀態。 

日誌重寫(Log rewriting) 
你可以猜得到,寫操作不斷執行的時候AOF檔案會越來越大。例如,如果你增加一個計數器100次,你的資料集裡只會有一個鍵儲存這最終值,但是卻有100條記錄在AOF中。其中99條記錄在重建當前狀態時是不需要的。 
於是Redis支援一個有趣的特性:在後臺重建AOF而不影響服務客戶端。每當你傳送BGREWRITEAOF時,Redis將會寫入一個新的AOF檔案,包含重建當前記憶體中資料集所需的最短命令序列。如果你使用的是Redis 2.2的AOF,你需要不時的執行BGREWRITEAOF命令。Redis 2.4可以自動觸發日誌重寫(檢視Redis 2.4中的示例配置檔案以獲得更多資訊)。 

AOF永續性如何(How durable) 
你可以配置多久Redis會fsync資料到磁碟一次。有三個選項: 

每次一個新命令追加到AOF檔案中時執行fsync。非常非常慢,但是非常安全。
每秒執行fsync。夠快(2.4版本中差不多和快照一樣快),但是當災難來臨時會丟失1秒的資料。
從不執行fsync,直接將你的資料交到作業系統手裡。更快,但是更不安全。

建議的(也是預設的)策略是每秒執行一次fsync。既快,也相當安全。一直執行的策略在實踐中非常慢(儘管在Redis 2.0中有所改進),因為沒法讓fsync這個操作本身更快。 

AOF損壞了怎麼辦(corrupted) 
有可能在寫AOF檔案時伺服器崩潰(crash),檔案損壞後Redis就無法裝載了。如果這個發生的話,你可以使用下面的步驟來解決這個問題: 

建立AOF的一個拷貝用於備份。
使用Redis自帶的redis-check-aof工具來修復原檔案:
$ redis-check-aof –fix
使用diff -u來檢查兩個檔案有什麼不同。
用修復好的檔案來重啟伺服器。

如何工作(How works) 
日誌重寫採用了和快照一樣的寫時複製機制。下面是過程: 

Redis呼叫fork()。於是我們有了父子兩個程式。
子程式開始向一個臨時檔案中寫AOF。
父程式在一個記憶體緩衝區中積累新的變更(同時將新的變更寫入舊的AOF檔案,所以即使重寫失敗我們也安全)。
當子程式完成重寫檔案,父程式收到一個訊號,追加記憶體緩衝區到子程式建立的檔案末尾。
搞定!現在Redis自動重新命名舊檔案為新的,然後開始追加新資料到新檔案。

如何從RDB切換到AOF(How switch) 
在Redis 2.2及以上版本中非常簡單,也不需要重啟。 

備份你最新的dump.rdb檔案。
把備份檔案放到一個安全的地方。
傳送以下兩個命令:
redis-cli config set appendonly yes
redis-cli config set save “”
確保你的資料庫含有其包含的相同的鍵的數量。
確保寫被正確的追加到AOF檔案。

第一個CONFIG命令開啟AOF。Redis會阻塞以生成初始轉儲檔案,然後開啟檔案準備寫,開始追加寫操作。 
第二個CONFIG命令用於關閉快照持久化。這一步是可選的,如果你想同時開啟這兩種持久化方法。 
重要:記得編輯你的redis.conf檔案來開啟AOF,否則當你重啟伺服器時,你的配置修改將會丟失,伺服器又會使用舊的配置。 

此處省略一萬字。。。。。。原文此處介紹2.0老版本怎麼操作。 

AOF和RDB的相互作用(Interactions) 
Redis 2.4及以後的版本中,不允許在RDB快照操作執行過程中觸發AOF重寫,也不允許在AOF重寫執行過程中執行BGSAVE。這防止了兩個Redis後臺程式同時對磁碟進行繁重的IO操作。 
當在快照執行的過程中,使用者使用BGREWRITEAOF顯式請求日誌重寫操作的話,伺服器會答覆一個OK狀態碼,告訴使用者這個操作已經被安排排程,等到快照完成時開始重寫。 
Redis在同時開啟AOF和RDB的情況下重啟,會使用AOF檔案來重建原始資料集,因為通常AOF檔案是儲存資料最完整的。 

備份資料(Backing up) 
開始這一部分之前,請務必牢記:一定要備份你的資料庫。磁碟損壞,雲中例項丟失,等等:沒有備份意味著資料丟失的巨大風險。 
Redis對資料備份非常友好,因為你可以在資料庫執行時拷貝RDB檔案:RDB檔案一旦生成就不會被修改,檔案生成到一個臨時檔案中,當新的快照完成後,將自動使用rename(2)原子性的修改檔名為目標檔案。 
這意味著,在伺服器執行時拷貝RDB檔案是完全安全的。以下是我們的建議: 

建立一個定時任務(cron job),每隔一個小時建立一個RDB快照到一個目錄,每天的快照放在另外一個目錄。
每次定時指令碼執行時,務必使用find命令來刪除舊的快照:例如,你可以儲存最近48小時內的每小時快照,一到兩個月的內的每天快照。注意命名快照時加上日期時間資訊。
至少每天一次將你的RDB快照傳輸到你的資料中心之外,或者至少傳輸到執行你的Redis例項的物理機之外。

災難恢復(Disaster recovery) 
在Redis中災難恢復基本上就是指備份,以及將這些備份傳輸到外部的多個資料中心。這樣即使一些災難性的事件影響到執行Redis和生成快照的主資料中心,資料也是安全的。 
由於許多Redis使用者都是啟動階段的屌絲,沒有太多錢花,我們會介紹一些最有意思的災難恢復技術,而不用太多的花銷。 

Amazon S3和一些類似的服務是幫助你災難恢復系統的一個好辦法。只需要將你的每日或每小時的RDB快照以加密的方式傳輸到S3.你可以使用gpg -c來加密你的資料(以對稱加密模式)。確保將你的密碼儲存在不同的安全地方(例如給一份到你的組織中的最重要的人)。推薦使用多個儲存服務來改進資料安全。
使用SCP(SSH的組成部分)來傳輸你的快照到遠端伺服器。這是一種相當簡單和安全的方式:在遠離你的位置搞一個小的VPS,安裝ssh,生成一個無口令的ssh客戶端key,並將其新增到你的VPS上的authorized_keys檔案中。你就可以自動的傳輸備份檔案了。為了達到好的效果,最好是至少從不同的提供商那搞兩個VPS。

要知道這種系統如果沒有正確的處理會很容易失敗。至少一定要確保傳輸完成後驗證檔案的大小(要匹配你拷貝的檔案),如果你使用VPS的話,可以使用SHA1摘要。 
你還需要一個某種獨立的告警系統,在某些原因導致的傳輸備份過程不正常時告警。

相關文章