Redis 持久化 Redis 提供了不同持久化範圍的選項: RDB 持久化以指定的時間間隔執行數據集的即時點(point-in-time)快照。 AOF 持久化在服務端記錄每次收到的寫操作,在伺服器啟動時會重放,以重建原始數據集。命令使用和 Redis 協議一樣的格式以追加的方式來記錄。當文件太大
Redis 持久化
Redis 提供了不同持久化範圍的選項:
- RDB 持久化以指定的時間間隔執行數據集的即時點(point-in-time)快照。
- AOF 持久化在服務端記錄每次收到的寫操作,在伺服器啟動時會重放,以重建原始數據集。命令使用和 Redis 協議一樣的格式以追加的方式來記錄。當文件太大時 Redis 會在後臺重寫日誌。
- 如果你願意,你可以完全禁止持久化,如果你只是希望你的數據在伺服器運行期間才存在的話。
- 可以在同一個實例上同時支持 AOF 和 RDB。註意,在這種情況下,當 Redis 重啟時,AOF 文件會被用於重建原始數據集,因為它被保證是最完整的數據。
理解 RDB 和 AOF 持久化之間的各自優劣 (trade-offs) 是一件非常重要的事情。讓我們先從 RDB 開始:
RDB 優點
- RDB 是一種表示某個即時點的 Redis 數據的緊湊文件。RDB 文件適合用於備份。例如,你可能想要每小時歸檔最近 24 小時的 RDB 文件,每天保存近 30 天的 RDB 快照。這允許你很容易的恢復不同版本的數據集以容災。
- RDB 非常適合於災難恢復,作為一個緊湊的單一文件,可以被傳輸到遠程的數據中心,或者是 Amazon S3(可能得加密)。
- RDB 最大化了 Redis 的性能,因為 Redis 父進程持久化時唯一需要做的是啟動(fork)一個子進程,由子進程完成所有剩餘工作。父進程實例不需要執行像磁碟 IO 這樣的操作。
- RDB 在重啟保存了大數據集的實例時比 AOF 要快。
RDB 缺點
當你需要在 Redis 停止工作(例如停電)時最小化數據丟失,RDB 可能不太好。你可以配置不同的保存點(save point)來保存 RDB 文件(例如,至少 5 分鐘和對數據集 100 次寫之後,但是你可以有多個保存點)。然而,你通常每隔 5 分鐘或更久創建一個 RDB 快照,所以一旦 Redis 因為任何原因沒有正確關閉而停止工作,你就得做好最近幾分鐘數據丟失的準備了。
RDB 需要經常調用 fork()子進程來持久化到磁碟。如果數據集很大的話,fork()比較耗時,結果就是,當數據集非常大並且 CPU 性能不夠強大的話,Redis 會停止服務客戶端幾毫秒甚至一秒。AOF 也需要 fork(),但是你可以調整多久頻率重寫日誌而不會有損(trade-off)持久性(durability)。
AOF 優點
- 使用 AOF Redis 會更具有可持久性(durable):你可以有很多不同的 fsync 策略:沒有 fsync,每秒 fsync,每次請求時 fsync。使用預設的每秒 fsync 策略,寫性能也仍然很不錯(fsync 是由後臺線程完成的,主線程繼續努力地執行寫請求),即便你也就僅僅只損失一秒鐘的寫數據。
- AOF 日誌是一個追加文件,所以不需要定位,在斷電時也沒有損壞問題。即使由於某種原因文件末尾是一個寫到一半的命令(磁碟滿或者其他原因),redis-check-aof 工具也可以很輕易的修複。
- 當 AOF 文件變得很大時,Redis 會自動在後臺進行重寫。重寫是絕對安全的,因為 Redis 繼續往舊的文件中追加,使用創建當前數據集所需的最小操作集合來創建一個全新的文件,一旦第二個文件創建完畢,Redis 就會切換這兩個文件,並開始往新文件追加。
- AOF 文件裡面包含一個接一個的操作,以易於理解和解析的格式存儲。你也可以輕易的導出一個 AOF 文件。例如,即使你不小心錯誤地使用 FLUSHALL 命令清空一切,如果此時並沒有執行重寫,你仍然可以保存你的數據集,你只要停止伺服器,刪除最後一條命令,然後重啟 Redis 就可以。
AOF 缺點
- 對同樣的數據集,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)我們還沒有收到一份用戶在真實世界中檢測到崩潰的報告。
該選誰
通常來說,你應該同時使用這兩種持久化方法,以達到和 PostgreSQL 提供的一樣的數據安全程度。
如果你很關註你的數據,但是仍然可以接受災難時有幾分鐘的數據丟失,你可以只單獨使用 RDB。
有很多用戶單獨使用 AOF,但是我們並不鼓勵這樣,因為時常進行 RDB 快照非常方便於資料庫備份,啟動速度也較之快,還避免了 AOF 引擎的 bug。
快照
預設情況下,Redis 保存數據集快照到磁碟,名為 dump.rdb 的二進位文件。你可以設置讓 Redis 在 N 秒內至少有 M 次數據集改動時保存數據集,或者你也可以手動調用 SAVE 或者 BGSAVE 命令。
例如,這個配置會讓 Redis 在每個 60 秒內至少有 1000 次鍵改動時自動轉儲數據集到磁碟:
save 60 1000
這種策略被稱為快照。
如何工作
每當 Redis 需要轉儲數據集到磁碟時,會發生:
- Redis 調用 fork()。於是我們有了父子兩個進程。
- 子進程開始將數據集寫入一個臨時 RDB 文件。
- 當子進程完成了新 RDB 文件,替換掉舊文件。
這個方法可以讓 Redis 獲益於寫時複製(copy-on-write)機制。
只追加文件
快照並不是非常具有可持久性(durable)。如果你運行 Redis 的電腦停機了,電源線斷了,或者你不小心 kill -9 掉你的實例,最近寫入 Redis 的數據將會丟失。儘管這個對一些應用程式來說不是什麼大事,但是也有一些需要完全可持久性(durability)的場景,在這些場景下可能就不合適了。
只追加文件是一個替代方案,是 Redis 的完全可持久性策略。在 1.1 版本中就可用了。
你可以在你的配置文件中開啟 AOF:
appendonly yes
從現在開始,每次 Redis 收到修改數據集的命令,將會被追加到 AOF 中。當你重啟 Redis 的時候,就會重放(re-play)AOF 文件來重建狀態。
日誌重寫
你可以猜得到,寫操作不斷執行的時候 AOF 文件會越來越大。例如,如果你增加一個計數器 100 次,你的數據集里只會有一個鍵存儲這最終值,但是卻有 100 條記錄在 AOF 中。其中 99 條記錄在重建當前狀態時是不需要的。
於是 Redis 支持一個有趣的特性:在後臺重建 AOF 而不影響服務客戶端。每當你發送 BGREWRITEAOF 時,Redis 將會寫入一個新的 AOF 文件,包含重建當前記憶體中數據集所需的最短命令序列。如果你使用的是 Redis 2.2 的 AOF,你需要不時的運行 BGREWRITEAOF 命令。Redis 2.4 可以自動觸發日誌重寫(查看 Redis 2.4 中的示例配置文件以獲得更多信息)。
AOF 持久性如何
你可以配置多久 Redis 會 fsync 數據到磁碟一次。有三個選項:
- 每次一個新命令追加到 AOF 文件中時執行 fsync。非常非常慢,但是非常安全。
- 每秒執行 fsync。夠快(2.4 版本中差不多和快照一樣快),但是當災難來臨時會丟失 1 秒的數據。
- 從不執行 fsync,直接將你的數據交到操作系統手裡。更快,但是更不安全。
建議的(也是預設的)策略是每秒執行一次 fsync。既快,也相當安全。一直執行的策略在實踐中非常慢(儘管在 Redis 2.0 中有所改進),因為沒法讓 fsync 這個操作本身更快。
AOF 損壞了怎麼辦
有可能在寫 AOF 文件時伺服器崩潰(crash),文件損壞後 Redis 就無法裝載了。如果這個發生的話,你可以使用下麵的步驟來解決這個問題:
- 創建 AOF 的一個拷貝用於備份。
- 使用 Redis 自帶的 redis-check-aof 工具來修複原文件:
- $ redis-check-aof --fix
- 使用 diff -u 來檢查兩個文件有什麼不同。用修複好的文件來重啟伺服器。
如何工作
日誌重寫採用了和快照一樣的寫時複製機制。下麵是過程:
- Redis 調用 fork()。於是我們有了父子兩個進程。
- 子進程開始向一個臨時文件中寫 AOF。
- 父進程在一個記憶體緩衝區中積累新的變更(同時將新的變更寫入舊的 AOF 文件,所以即使重寫失敗我們也安全)。
- 當子進程完成重寫文件,父進程收到一個信號,追加記憶體緩衝區到子進程創建的文件末尾。
- 搞定!現在 Redis 自動重命名舊文件為新的,然後開始追加新數據到新文件。
如何從 RDB 切換到 AOF
在 Redis 2.2 及以上版本中非常簡單,也不需要重啟。
- 備份你最新的 dump.rdb 文件。
- 把備份文件放到一個安全的地方。
- 發送以下兩個命令:
- redis-cli config set appendonly yes
- redis-cli config set save ""
- 確保你的資料庫含有其包含的相同的鍵的數量。
- 確保寫被正確的追加到 AOF 文件。
第一個 CONFIG 命令開啟 AOF。Redis 會阻塞以生成初始轉儲文件,然後打開文件準備寫,開始追加寫操作。
第二個 CONFIG 命令用於關閉快照持久化。這一步是可選的,如果你想同時開啟這兩種持久化方法。
重要:記得編輯你的 redis.conf 文件來開啟 AOF,否則當你重啟伺服器時,你的配置修改將會丟失,伺服器又會使用舊的配置。
AOF 和 RDB 的相互作用
Redis 2.4 及以後的版本中,不允許在 RDB 快照操作運行過程中觸發 AOF 重寫,也不允許在 AOF 重寫運行過程中運行 BGSAVE。這防止了兩個 Redis 後臺進程同時對磁碟進行繁重的 IO 操作。
當在快照運行的過程中,用戶使用 BGREWRITEAOF 顯式請求日誌重寫操作的話,伺服器會答覆一個 OK 狀態碼,告訴用戶這個操作已經被安排調度,等到快照完成時開始重寫。
Redis 在同時開啟 AOF 和 RDB 的情況下重啟,會使用 AOF 文件來重建原始數據集,因為通常 AOF 文件是保存數據最完整的。
備份數據
一定要備份你的資料庫。磁碟損壞,雲中實例丟失,等等:沒有備份意味著數據丟失的巨大風險。
Redis 對數據備份非常友好,因為你可以在資料庫運行時拷貝 RDB 文件:RDB 文件一旦生成就不會被修改,文件生成到一個臨時文件中,當新的快照完成後,將自動使用 rename(2) 原子性的修改文件名為目標文件。
這意味著,在伺服器運行時拷貝 RDB 文件是完全安全的。以下是我們的建議:
- 創建一個定時任務(cron job),每隔一個小時創建一個 RDB 快照到一個目錄,每天的快照放在另外一個目錄。
- 每次定時腳本運行時,務必使用 find 命令來刪除舊的快照:例如,你可以保存最近 48 小時內的每小時快照,一到兩個月的內的每天快照。註意命名快照時加上日期時間信息。
- 至少每天一次將你的 RDB 快照傳輸到你的數據中心之外,或者至少傳輸到運行你的 Redis 實例的物理機之外。
災難恢復
在 Redis 中災難恢復基本上就是指備份,以及將這些備份傳輸到外部的多個數據中心。這樣即使一些災難性的事件影響到運行 Redis 和生成快照的主數據中心,數據也是安全的。
由於許多 Redis 用戶都是啟動階段的屌絲,沒有太多錢花,我們會介紹一些最有意思的災難恢復技術,而不用太多的花銷。
- Amazon S3 和一些類似的服務是幫助你災難恢復系統的一個好辦法。只需要將你的每日或每小時的 RDB 快照以加密的方式傳輸到 S3。你可以使用 gpg -c 來加密你的數據(以對稱加密模式)。確保將你的密碼保存在不同的安全地方(例如給一份到你的組織中的最重要的人)。推薦使用多個存儲服務來改進數據安全。
- 使用 SCP(SSH 的組成部分)來傳輸你的快照到遠程伺服器。這是一種相當簡單和安全的方式:在遠離你的位置搞一個小的 VPS,安裝 ssh,生成一個無口令的 ssh 客戶端 key,並將其添加到你的 VPS 上的 authorized_keys 文件中。你就可以自動的傳輸備份文件了。為了達到好的效果,最好是至少從不同的提供商那搞兩個 VPS。
要知道這種系統如果沒有正確的處理會很容易失敗。至少一定要確保傳輸完成後驗證文件的大小 (要匹配你拷貝的文件),如果你使用 VPS 的話,可以使用 SHA1 摘要。
你還需要一個某種獨立的告警系統,在某些原因導致的傳輸備份過程不正常時告警。