本文提供對 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é)議一樣的格式以追加的方式來記錄。當(dāng)文件太大時 Redis 會在后臺重寫日志。
- 如果你愿意,你可以完全禁止持久化,如果你只是希望你的數(shù)據(jù)在服務(wù)器運行期間才存在的話。
- 可以在同一個實例上同時支持 AOF 和 RDB。注意,在這種情況下,當(dāng) 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 快照。這允許你很容易的恢復(fù)不同版本的數(shù)據(jù)集以容災(zāi)。
- RDB 非常適合于災(zāi)難恢復(fù),作為一個緊湊的單一文件,可以被傳輸?shù)竭h(yuǎn)程的數(shù)據(jù)中心,或者是 Amazon S3(可能得加密)。
- RDB 最大化了 Redis 的性能,因為 Redis 父進(jìn)程持久化時唯一需要做的是啟動(fork)一個子進(jìn)程,由子進(jìn)程完成所有剩余工作。父進(jìn)程實例不需要執(zhí)行像磁盤 IO 這樣的操作。
- RDB 在重啟保存了大數(shù)據(jù)集的實例時比 AOF 要快。
RDB 缺點(RDB disadvantages)
當(dāng)你需要在 Redis 停止工作(例如停電)時最小化數(shù)據(jù)丟失,RDB 可能不太好。你可以配置不同的保存點(save point)來保存 RDB 文件(例如,至少 5 分鐘和對數(shù)據(jù)集 100 次寫之后,但是你可以有多個保存點)。然而,你通常每隔 5 分鐘或更久創(chuàng)建一個 RDB 快照,所以一旦 Redis 因為任何原因沒有正確關(guān)閉而停止工作,你就得做好最近幾分鐘數(shù)據(jù)丟失的準(zhǔn)備了。
RDB 需要經(jīng)常調(diào)用 fork()子進(jìn)程來持久化到磁盤。如果數(shù)據(jù)集很大的話,fork()比較耗時,結(jié)果就是,當(dāng)數(shù)據(jù)集非常大并且 CPU 性能不夠強(qiáng)大的話,Redis 會停止服務(wù)客戶端幾毫秒甚至一秒。AOF 也需要 fork(),但是你可以調(diào)整多久頻率重寫日志而不會有損(trade-off)持久性(durability)。
AOF 優(yōu)點(AOF advantages)
- 使用 AOF Redis 會更具有可持久性(durable):你可以有很多不同的 fsync 策略:沒有 fsync,每秒 fsync,每次請求時 fsync。使用默認(rèn)的每秒 fsync 策略,寫性能也仍然很不錯(fsync 是由后臺線程完成的,主線程繼續(xù)努力地執(zhí)行寫請求),即便你也就僅僅只損失一秒鐘的寫數(shù)據(jù)。
- AOF 日志是一個追加文件,所以不需要定位,在斷電時也沒有損壞問題。即使由于某種原因文件末尾是一個寫到一半的命令(磁盤滿或者其他原因),redis-check-aof 工具也可以很輕易的修復(fù)。
- 當(dāng) AOF 文件變得很大時,Redis 會自動在后臺進(jìn)行重寫。重寫是絕對安全的,因為 Redis 繼續(xù)往舊的文件中追加,使用創(chuàng)建當(dāng)前數(shù)據(jù)集所需的最小操作集合來創(chuàng)建一個全新的文件,一旦第二個文件創(chuàng)建完畢,Redis 就會切換這兩個文件,并開始往新文件追加。
- AOF 文件里面包含一個接一個的操作,以易于理解和解析的格式存儲。你也可以輕易的導(dǎo)出一個 AOF 文件。例如,即使你不小心錯誤地使用 FLUSHALL 命令清空一切,如果此時并沒有執(zhí)行重寫,你仍然可以保存你的數(shù)據(jù)集,你只要停止服務(wù)器,刪除最后一條命令,然后重啟 Redis 就可以。
AOF 缺點(AOF disadvantages)
- 對同樣的數(shù)據(jù)集,AOF 文件通常要大于等價的 RDB 文件。
- AOF 可能比 RDB 慢,這取決于準(zhǔn)確的 fsync 策略。通常 fsync 設(shè)置為每秒一次的話性能仍然很高,如果關(guān)閉 fsync,即使在很高的負(fù)載下也和 RDB 一樣的快。不過,即使在很大的寫負(fù)載情況下,RDB 還是能提供能好的最大延遲保證。
- 在過去,我們經(jīng)歷了一些針對特殊命令(例如,像 BRPOPLPUSH 這樣的阻塞命令)的罕見 bug,導(dǎo)致在數(shù)據(jù)加載時無法恢復(fù)到保存時的樣子。這些 bug 很罕見,我們也在測試套件中進(jìn)行了測試,自動隨機(jī)創(chuàng)造復(fù)雜的數(shù)據(jù)集,然后加載它們以檢查一切是否正常,但是,這類 bug 幾乎不可能出現(xiàn)在 RDB 持久化中。為了說得更清楚一點:Redis AOF 是通過遞增地更新一個已經(jīng)存在的狀態(tài),像 MySQL 或者 MongoDB 一樣,而 RDB 快照是一次又一次地從頭開始創(chuàng)造一切,概念上更健壯。但是,1)要注意 Redis 每次重寫 AOF 時都是以當(dāng)前數(shù)據(jù)集中的真實數(shù)據(jù)從頭開始,相對于一直追加的 AOF 文件(或者一次重寫讀取老的 AOF 文件而不是讀內(nèi)存中的數(shù)據(jù))對 bug 的免疫力更強(qiáng)。2)我們還沒有收到一份用戶在真實世界中檢測到崩潰的報告。
我們該選誰(what)
通常來說,你應(yīng)該同時使用這兩種持久化方法,以達(dá)到和 PostgreSQL 提供的一樣的數(shù)據(jù)安全程度。
如果你很關(guān)注你的數(shù)據(jù),但是仍然可以接受災(zāi)難時有幾分鐘的數(shù)據(jù)丟失,你可以只單獨使用 RDB。
有很多用戶單獨使用 AOF,但是我們并不鼓勵這樣,因為時常進(jìn)行 RDB 快照非常方便于數(shù)據(jù)庫備份,啟動速度也較之快,還避免了 AOF 引擎的 bug。
注意:基于這些原因,將來我們可能會統(tǒng)一 AOF 和 RDB 為一種單一的持久化模型(長遠(yuǎn)計劃)。
下面的部分將介紹兩種持久化模型等多的細(xì)節(jié)。
快照(Snapshotting)
默認(rèn)情況下,Redis 保存數(shù)據(jù)集快照到磁盤,名為 dump.rdb 的二進(jìn)制文件。你可以設(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)
每當(dāng) Redis 需要轉(zhuǎn)儲數(shù)據(jù)集到磁盤時,會發(fā)生:
- Redis 調(diào)用 fork()。于是我們有了父子兩個進(jìn)程。
- 子進(jìn)程開始將數(shù)據(jù)集寫入一個臨時 RDB 文件。
- 當(dāng)子進(jìn)程完成了新 RDB 文件,替換掉舊文件。
這個方法可以讓 Redis 獲益于寫時復(fù)制(copy-on-write)機(jī)制。
只追加文件(Append-only file)
快照并不是非常具有可持久性(durable)。如果你運行 Redis 的電腦停機(jī)了,電源線斷了,或者你不小心 kill -9 掉你的實例,最近寫入 Redis 的數(shù)據(jù)將會丟失。盡管這個對一些應(yīng)用程序來說不是什么大事,但是也有一些需要完全可持久性(durability)的場景,在這些場景下可能就不合適了。
只追加文件是一個替代方案,是 Redis 的完全可持久性策略。在 1.1 版本中就可用了。
你可以在你的配置文件中開啟 AOF:
appendonly yes
從現(xiàn)在開始,每次 Redis 收到修改數(shù)據(jù)集的命令,將會被追加到 AOF 中。當(dāng)你重啟 Redis 的時候,就會重放(re-play)AOF 文件來重建狀態(tài)。
日志重寫(Log rewriting)
你可以猜得到,寫操作不斷執(zhí)行的時候 AOF 文件會越來越大。例如,如果你增加一個計數(shù)器 100 次,你的數(shù)據(jù)集里只會有一個鍵存儲這最終值,但是卻有 100 條記錄在 AOF 中。其中 99 條記錄在重建當(dāng)前狀態(tài)時是不需要的。
于是 Redis 支持一個有趣的特性:在后臺重建 AOF 而不影響服務(wù)客戶端。每當(dāng)你發(fā)送 BGREWRITEAOF 時,Redis 將會寫入一個新的 AOF 文件,包含重建當(dāng)前內(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 版本中差不多和快照一樣快),但是當(dāng)災(zāi)難來臨時會丟失 1 秒的數(shù)據(jù)。
- 從不執(zhí)行 fsync,直接將你的數(shù)據(jù)交到操作系統(tǒng)手里。更快,但是更不安全。
建議的(也是默認(rèn)的)策略是每秒執(zhí)行一次 fsync。既快,也相當(dāng)安全。一直執(zhí)行的策略在實踐中非常慢(盡管在 Redis 2.0 中有所改進(jìn)),因為沒法讓 fsync 這個操作本身更快。
AOF 損壞了怎么辦(corrupted)
有可能在寫 AOF 文件時服務(wù)器崩潰(crash),文件損壞后 Redis 就無法裝載了。如果這個發(fā)生的話,你可以使用下面的步驟來解決這個問題:
- 創(chuàng)建 AOF 的一個拷貝用于備份。
- 使用 Redis 自帶的 redis-check-aof 工具來修復(fù)原文件:
- $ redis-check-aof --fix
- 使用 diff -u 來檢查兩個文件有什么不同。用修復(fù)好的文件來重啟服務(wù)器。
如何工作(How works)
日志重寫采用了和快照一樣的寫時復(fù)制機(jī)制。下面是過程:
- Redis 調(diào)用 fork()。于是我們有了父子兩個進(jìn)程。
- 子進(jìn)程開始向一個臨時文件中寫 AOF。
- 父進(jìn)程在一個內(nèi)存緩沖區(qū)中積累新的變更(同時將新的變更寫入舊的 AOF 文件,所以即使重寫失敗我們也安全)。
- 當(dāng)子進(jìn)程完成重寫文件,父進(jìn)程收到一個信號,追加內(nèi)存緩沖區(qū)到子進(jìn)程創(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)儲文件,然后打開文件準(zhǔn)備寫,開始追加寫操作。
第二個 CONFIG 命令用于關(guān)閉快照持久化。這一步是可選的,如果你想同時開啟這兩種持久化方法。
重要:記得編輯你的 redis.conf 文件來開啟 AOF,否則當(dāng)你重啟服務(wù)器時,你的配置修改將會丟失,服務(wù)器又會使用舊的配置。
此處省略一萬字。。。。。。原文此處介紹 2.0 老版本怎么操作。
AOF 和 RDB 的相互作用(Interactions)
Redis 2.4 及以后的版本中,不允許在 RDB 快照操作運行過程中觸發(fā) AOF 重寫,也不允許在 AOF 重寫運行過程中運行 BGSAVE。這防止了兩個 Redis 后臺進(jìn)程同時對磁盤進(jìn)行繁重的 IO 操作。
當(dāng)在快照運行的過程中,用戶使用 BGREWRITEAOF 顯式請求日志重寫操作的話,服務(wù)器會答復(fù)一個 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ù)丟失的巨大風(fēng)險。
Redis 對數(shù)據(jù)備份非常友好,因為你可以在數(shù)據(jù)庫運行時拷貝 RDB 文件:RDB 文件一旦生成就不會被修改,文件生成到一個臨時文件中,當(dāng)新的快照完成后,將自動使用 rename(2) 原子性的修改文件名為目標(biāo)文件。
這意味著,在服務(wù)器運行時拷貝 RDB 文件是完全安全的。以下是我們的建議:
- 創(chuàng)建一個定時任務(wù)(cron job),每隔一個小時創(chuàng)建一個 RDB 快照到一個目錄,每天的快照放在另外一個目錄。
- 每次定時腳本運行時,務(wù)必使用 find 命令來刪除舊的快照:例如,你可以保存最近 48 小時內(nèi)的每小時快照,一到兩個月的內(nèi)的每天快照。注意命名快照時加上日期時間信息。
- 至少每天一次將你的 RDB 快照傳輸?shù)侥愕臄?shù)據(jù)中心之外,或者至少傳輸?shù)竭\行你的 Redis 實例的物理機(jī)之外。
災(zāi)難恢復(fù)(Disaster recovery)
在 Redis 中災(zāi)難恢復(fù)基本上就是指備份,以及將這些備份傳輸?shù)酵獠康亩鄠€數(shù)據(jù)中心。這樣即使一些災(zāi)難性的事件影響到運行 Redis 和生成快照的主數(shù)據(jù)中心,數(shù)據(jù)也是安全的。
由于許多 Redis 用戶都是啟動階段的屌絲,沒有太多錢花,我們會介紹一些最有意思的災(zāi)難恢復(fù)技術(shù),而不用太多的花銷。
- Amazon S3 和一些類似的服務(wù)是幫助你災(zāi)難恢復(fù)系統(tǒng)的一個好辦法。只需要將你的每日或每小時的 RDB 快照以加密的方式傳輸?shù)?S3。你可以使用 gpg -c 來加密你的數(shù)據(jù)(以對稱加密模式)。確保將你的密碼保存在不同的安全地方(例如給一份到你的組織中的最重要的人)。推薦使用多個存儲服務(wù)來改進(jìn)數(shù)據(jù)安全。
- 使用 SCP(SSH 的組成部分)來傳輸你的快照到遠(yuǎn)程服務(wù)器。這是一種相當(dāng)簡單和安全的方式:在遠(yuǎn)離你的位置搞一個小的 VPS,安裝 ssh,生成一個無口令的 ssh 客戶端 key,并將其添加到你的 VPS 上的 authorized_keys 文件中。你就可以自動的傳輸備份文件了。為了達(dá)到好的效果,最好是至少從不同的提供商那搞兩個 VPS。
要知道這種系統(tǒng)如果沒有正確的處理會很容易失敗。至少一定要確保傳輸完成后驗證文件的大小 (要匹配你拷貝的文件),如果你使用 VPS 的話,可以使用 SHA1 摘要。
你還需要一個某種獨立的告警系統(tǒng),在某些原因?qū)е碌膫鬏攤浞葸^程不正常時告警。