鍍金池/ 教程/ 大數(shù)據(jù)/ 持久化
使用 Redis 實現(xiàn) Twitter(上)
集群(下)
使用 Redis 實現(xiàn) Twitter(下)
使用 Redis 作為 LRU 緩存
高可用(上)
高可用客戶端指引
集群(中)
高可用(下)
持久化
Redis 介紹
集中插入
集群(上)
從入門到精通(上)
從入門到精通(下)
從入門到精通(中)
分片
數(shù)據(jù)類型初探
復制

持久化

本文提供對 Redis 持久化(persistence)的技術(shù)性描述,適合所有的 Redis 用戶來閱讀。想獲得對 Redis 持久化和持久性保證有更全面的了解,也可以讀一下作者的博客文章(地址為 http://antirez.com/post/redis-persistence-demystified.html,譯者注)。

Redis 持久化(Persistence)

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

  • RDB 持久化以指定的時間間隔執(zhí)行數(shù)據(jù)集的即時點(point-in-time)快照。
  • AOF 持久化在服務(wù)端記錄每次收到的寫操作,在服務(wù)器啟動時會重放,以重建原始數(shù)據(jù)集。命令使用和 Redis 協(xié)議一樣的格式以追加的方式來記錄。當文件太大時 Redis 會在后臺重寫日志。
  • 如果你愿意,你可以完全禁止持久化,如果你只是希望你的數(shù)據(jù)在服務(wù)器運行期間才存在的話。
  • 可以在同一個實例上同時支持 AOF 和 RDB。注意,在這種情況下,當 Redis 重啟時,AOF 文件會被用于重建原始數(shù)據(jù)集,因為它被保證是最完整的數(shù)據(jù)。

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

RDB 優(yōu)點(RDB advantages)

  • RDB 是一種表示某個即時點的 Redis 數(shù)據(jù)的緊湊文件。RDB 文件適合用于備份。例如,你可能想要每小時歸檔最近 24 小時的 RDB 文件,每天保存近 30 天的 RDB 快照。這允許你很容易的恢復不同版本的數(shù)據(jù)集以容災(zāi)。
  • RDB 非常適合于災(zāi)難恢復,作為一個緊湊的單一文件,可以被傳輸?shù)竭h程的數(shù)據(jù)中心,或者是 Amazon S3(可能得加密)。
  • RDB 最大化了 Redis 的性能,因為 Redis 父進程持久化時唯一需要做的是啟動(fork)一個子進程,由子進程完成所有剩余工作。父進程實例不需要執(zhí)行像磁盤 IO 這樣的操作。
  • RDB 在重啟保存了大數(shù)據(jù)集的實例時比 AOF 要快。

RDB 缺點(RDB disadvantages)

當你需要在 Redis 停止工作(例如停電)時最小化數(shù)據(jù)丟失,RDB 可能不太好。你可以配置不同的保存點(save point)來保存 RDB 文件(例如,至少 5 分鐘和對數(shù)據(jù)集 100 次寫之后,但是你可以有多個保存點)。然而,你通常每隔 5 分鐘或更久創(chuàng)建一個 RDB 快照,所以一旦 Redis 因為任何原因沒有正確關(guān)閉而停止工作,你就得做好最近幾分鐘數(shù)據(jù)丟失的準備了。

RDB 需要經(jīng)常調(diào)用 fork()子進程來持久化到磁盤。如果數(shù)據(jù)集很大的話,fork()比較耗時,結(jié)果就是,當數(shù)據(jù)集非常大并且 CPU 性能不夠強大的話,Redis 會停止服務(wù)客戶端幾毫秒甚至一秒。AOF 也需要 fork(),但是你可以調(diào)整多久頻率重寫日志而不會有損(trade-off)持久性(durability)。

AOF 優(yōu)點(AOF advantages)

  • 使用 AOF Redis 會更具有可持久性(durable):你可以有很多不同的 fsync 策略:沒有 fsync,每秒 fsync,每次請求時 fsync。使用默認的每秒 fsync 策略,寫性能也仍然很不錯(fsync 是由后臺線程完成的,主線程繼續(xù)努力地執(zhí)行寫請求),即便你也就僅僅只損失一秒鐘的寫數(shù)據(jù)。
  • AOF 日志是一個追加文件,所以不需要定位,在斷電時也沒有損壞問題。即使由于某種原因文件末尾是一個寫到一半的命令(磁盤滿或者其他原因),redis-check-aof 工具也可以很輕易的修復。
  • 當 AOF 文件變得很大時,Redis 會自動在后臺進行重寫。重寫是絕對安全的,因為 Redis 繼續(xù)往舊的文件中追加,使用創(chuàng)建當前數(shù)據(jù)集所需的最小操作集合來創(chuàng)建一個全新的文件,一旦第二個文件創(chuàng)建完畢,Redis 就會切換這兩個文件,并開始往新文件追加。
  • AOF 文件里面包含一個接一個的操作,以易于理解和解析的格式存儲。你也可以輕易的導出一個 AOF 文件。例如,即使你不小心錯誤地使用 FLUSHALL 命令清空一切,如果此時并沒有執(zhí)行重寫,你仍然可以保存你的數(shù)據(jù)集,你只要停止服務(wù)器,刪除最后一條命令,然后重啟 Redis 就可以。

AOF 缺點(AOF disadvantages)

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

我們該選誰(what)

通常來說,你應(yīng)該同時使用這兩種持久化方法,以達到和 PostgreSQL 提供的一樣的數(shù)據(jù)安全程度。

如果你很關(guān)注你的數(shù)據(jù),但是仍然可以接受災(zāi)難時有幾分鐘的數(shù)據(jù)丟失,你可以只單獨使用 RDB。

有很多用戶單獨使用 AOF,但是我們并不鼓勵這樣,因為時常進行 RDB 快照非常方便于數(shù)據(jù)庫備份,啟動速度也較之快,還避免了 AOF 引擎的 bug。

注意:基于這些原因,將來我們可能會統(tǒng)一 AOF 和 RDB 為一種單一的持久化模型(長遠計劃)。

下面的部分將介紹兩種持久化模型等多的細節(jié)。

快照(Snapshotting)

默認情況下,Redis 保存數(shù)據(jù)集快照到磁盤,名為 dump.rdb 的二進制文件。你可以設(shè)置讓 Redis 在 N 秒內(nèi)至少有 M 次數(shù)據(jù)集改動時保存數(shù)據(jù)集,或者你也可以手動調(diào)用 SAVE 或者 BGSAVE 命令。

例如,這個配置會讓 Redis 在每個 60 秒內(nèi)至少有 1000 次鍵改動時自動轉(zhuǎn)儲數(shù)據(jù)集到磁盤:

save 60 1000  

這種策略被稱為快照。

如何工作(How works)

每當 Redis 需要轉(zhuǎn)儲數(shù)據(jù)集到磁盤時,會發(fā)生:

  • Redis 調(diào)用 fork()。于是我們有了父子兩個進程。
  • 子進程開始將數(shù)據(jù)集寫入一個臨時 RDB 文件。
  • 當子進程完成了新 RDB 文件,替換掉舊文件。

這個方法可以讓 Redis 獲益于寫時復制(copy-on-write)機制。

只追加文件(Append-only file)

快照并不是非常具有可持久性(durable)。如果你運行 Redis 的電腦停機了,電源線斷了,或者你不小心 kill -9 掉你的實例,最近寫入 Redis 的數(shù)據(jù)將會丟失。盡管這個對一些應(yīng)用程序來說不是什么大事,但是也有一些需要完全可持久性(durability)的場景,在這些場景下可能就不合適了。

只追加文件是一個替代方案,是 Redis 的完全可持久性策略。在 1.1 版本中就可用了。

你可以在你的配置文件中開啟 AOF:

appendonly yes  

從現(xiàn)在開始,每次 Redis 收到修改數(shù)據(jù)集的命令,將會被追加到 AOF 中。當你重啟 Redis 的時候,就會重放(re-play)AOF 文件來重建狀態(tài)。

日志重寫(Log rewriting)

你可以猜得到,寫操作不斷執(zhí)行的時候 AOF 文件會越來越大。例如,如果你增加一個計數(shù)器 100 次,你的數(shù)據(jù)集里只會有一個鍵存儲這最終值,但是卻有 100 條記錄在 AOF 中。其中 99 條記錄在重建當前狀態(tài)時是不需要的。

于是 Redis 支持一個有趣的特性:在后臺重建 AOF 而不影響服務(wù)客戶端。每當你發(fā)送 BGREWRITEAOF 時,Redis 將會寫入一個新的 AOF 文件,包含重建當前內(nèi)存中數(shù)據(jù)集所需的最短命令序列。如果你使用的是 Redis 2.2 的 AOF,你需要不時的運行 BGREWRITEAOF 命令。Redis 2.4 可以自動觸發(fā)日志重寫(查看 Redis 2.4 中的示例配置文件以獲得更多信息)。

AOF 持久性如何(How durable)

你可以配置多久 Redis 會 fsync 數(shù)據(jù)到磁盤一次。有三個選項:

  • 每次一個新命令追加到 AOF 文件中時執(zhí)行 fsync。非常非常慢,但是非常安全。
  • 每秒執(zhí)行 fsync。夠快(2.4 版本中差不多和快照一樣快),但是當災(zāi)難來臨時會丟失 1 秒的數(shù)據(jù)。
  • 從不執(zhí)行 fsync,直接將你的數(shù)據(jù)交到操作系統(tǒng)手里。更快,但是更不安全。

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

AOF 損壞了怎么辦(corrupted)

有可能在寫 AOF 文件時服務(wù)器崩潰(crash),文件損壞后 Redis 就無法裝載了。如果這個發(fā)生的話,你可以使用下面的步驟來解決這個問題:

  • 創(chuàng)建 AOF 的一個拷貝用于備份。
  • 使用 Redis 自帶的 redis-check-aof 工具來修復原文件:
  • $ redis-check-aof --fix
  • 使用 diff -u 來檢查兩個文件有什么不同。用修復好的文件來重啟服務(wù)器。

如何工作(How works)

日志重寫采用了和快照一樣的寫時復制機制。下面是過程:

  • Redis 調(diào)用 fork()。于是我們有了父子兩個進程。
  • 子進程開始向一個臨時文件中寫 AOF。
  • 父進程在一個內(nèi)存緩沖區(qū)中積累新的變更(同時將新的變更寫入舊的 AOF 文件,所以即使重寫失敗我們也安全)。
  • 當子進程完成重寫文件,父進程收到一個信號,追加內(nèi)存緩沖區(qū)到子進程創(chuàng)建的文件末尾。
  • 搞定!現(xiàn)在 Redis 自動重命名舊文件為新的,然后開始追加新數(shù)據(jù)到新文件。

如何從 RDB 切換到 AOF(How switch)

在 Redis 2.2 及以上版本中非常簡單,也不需要重啟。

  • 備份你最新的 dump.rdb 文件。
  • 把備份文件放到一個安全的地方。
  • 發(fā)送以下兩個命令:
  • redis-cli config set appendonly yes
  • redis-cli config set save ""
  • 確保你的數(shù)據(jù)庫含有其包含的相同的鍵的數(shù)量。
  • 確保寫被正確的追加到 AOF 文件。

第一個 CONFIG 命令開啟 AOF。Redis 會阻塞以生成初始轉(zhuǎn)儲文件,然后打開文件準備寫,開始追加寫操作。

第二個 CONFIG 命令用于關(guān)閉快照持久化。這一步是可選的,如果你想同時開啟這兩種持久化方法。

重要:記得編輯你的 redis.conf 文件來開啟 AOF,否則當你重啟服務(wù)器時,你的配置修改將會丟失,服務(wù)器又會使用舊的配置。

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

AOF 和 RDB 的相互作用(Interactions)

Redis 2.4 及以后的版本中,不允許在 RDB 快照操作運行過程中觸發(fā) AOF 重寫,也不允許在 AOF 重寫運行過程中運行 BGSAVE。這防止了兩個 Redis 后臺進程同時對磁盤進行繁重的 IO 操作。

當在快照運行的過程中,用戶使用 BGREWRITEAOF 顯式請求日志重寫操作的話,服務(wù)器會答復一個 OK 狀態(tài)碼,告訴用戶這個操作已經(jīng)被安排調(diào)度,等到快照完成時開始重寫。

Redis 在同時開啟 AOF 和 RDB 的情況下重啟,會使用 AOF 文件來重建原始數(shù)據(jù)集,因為通常 AOF 文件是保存數(shù)據(jù)最完整的。

備份數(shù)據(jù)(Backing up)

開始這一部分之前,請務(wù)必牢記:一定要備份你的數(shù)據(jù)庫。磁盤損壞,云中實例丟失,等等:沒有備份意味著數(shù)據(jù)丟失的巨大風險。

Redis 對數(shù)據(jù)備份非常友好,因為你可以在數(shù)據(jù)庫運行時拷貝 RDB 文件:RDB 文件一旦生成就不會被修改,文件生成到一個臨時文件中,當新的快照完成后,將自動使用 rename(2) 原子性的修改文件名為目標文件。

這意味著,在服務(wù)器運行時拷貝 RDB 文件是完全安全的。以下是我們的建議:

  • 創(chuàng)建一個定時任務(wù)(cron job),每隔一個小時創(chuàng)建一個 RDB 快照到一個目錄,每天的快照放在另外一個目錄。
  • 每次定時腳本運行時,務(wù)必使用 find 命令來刪除舊的快照:例如,你可以保存最近 48 小時內(nèi)的每小時快照,一到兩個月的內(nèi)的每天快照。注意命名快照時加上日期時間信息。
  • 至少每天一次將你的 RDB 快照傳輸?shù)侥愕臄?shù)據(jù)中心之外,或者至少傳輸?shù)竭\行你的 Redis 實例的物理機之外。

災(zāi)難恢復(Disaster recovery)

在 Redis 中災(zāi)難恢復基本上就是指備份,以及將這些備份傳輸?shù)酵獠康亩鄠€數(shù)據(jù)中心。這樣即使一些災(zāi)難性的事件影響到運行 Redis 和生成快照的主數(shù)據(jù)中心,數(shù)據(jù)也是安全的。

由于許多 Redis 用戶都是啟動階段的屌絲,沒有太多錢花,我們會介紹一些最有意思的災(zāi)難恢復技術(shù),而不用太多的花銷。

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

要知道這種系統(tǒng)如果沒有正確的處理會很容易失敗。至少一定要確保傳輸完成后驗證文件的大小 (要匹配你拷貝的文件),如果你使用 VPS 的話,可以使用 SHA1 摘要。

你還需要一個某種獨立的告警系統(tǒng),在某些原因?qū)е碌膫鬏攤浞葸^程不正常時告警。