Redis持久化:AOF和RDB
持久化(persistence)
Redis 持久化
Redis 提供了多種不同級別的持久化方式:
- RDB 持久化可以在指定的時間間隔內(nèi)生成數(shù)據(jù)集的時間點快照(point-in-time snapshot)。
- AOF 持久化記錄服務(wù)器執(zhí)行的所有寫操作命令,并在服務(wù)器啟動時,通過重新執(zhí)行這些命令來還原數(shù)據(jù)集。AOF 文件中的命令全部以 Redis 協(xié)議的格式來保存,新命令會被追加到文件的末尾(每秒寫一次)。 Redis 還可以在后臺對AOF 文件進行重寫(rewrite),使得 AOF 文件的體積不會超出保存數(shù)據(jù)集狀態(tài)所需的實際大小。
- Redis 還可以同時使用 AOF 持久化和 RDB 持久化。在這種情況下,當(dāng) Redis 重啟時,它會優(yōu)先使用 AOF 文件來還原數(shù)據(jù)集, 因為 AOF 文件保存的數(shù)據(jù)集通常比 RDB 文件所保存的數(shù)據(jù)集更完整。
- 你甚至可以關(guān)閉持久化功能,讓數(shù)據(jù)只在服務(wù)器運行時存在。
了解 RDB 持久化和 AOF 持久化之間的異同是非常重要的,以下幾個小節(jié)將詳細地介紹這這兩種持久化功能,并對它們的相同和不同之處進行說明。
RDB 的優(yōu)點
- RDB 是一個非常緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數(shù)據(jù)集。 這種文件非常適合用于進行備份: 比如說,你可以在最近的 24 小時內(nèi),每小時備份一次 RDB 文件,并且在每個月的每一天,也備份一個 RDB 文件。 這樣的話,即使遇上問題,也可以隨時將數(shù)據(jù)集還原到不同的版本。
- RDB 非常適用于災(zāi)難恢復(fù)(disaster recovery)(多個備份):它只有一個文件,并且內(nèi)容都非常緊湊,可以(在加密后)將它傳送到別的數(shù)據(jù)中心。
- RDB 可以最大化 Redis 的性能:父進程在保存 RDB 文件時唯一要做的就是?(fork?)分出一個子進程,然后這個子進程就會處理接下來的所有保存工作,父進程無須執(zhí)行任何磁盤 I/O 操作。
- RDB 在恢復(fù)大數(shù)據(jù)集時的速度比 AOF 的恢復(fù)速度要快。
- (我們知道Redis對客戶端響應(yīng)請求的工作模型是單進程和單線程的,如果在主進程內(nèi)啟動一個線程,這樣會造成對數(shù)據(jù)的競爭條件,為了避免使用鎖降低性能。基于以上兩點這就是為什么Redis通過啟動一個進程來執(zhí)行RDB了。)
RDB 的缺點
- 如果你需要盡量避免在服務(wù)器故障時丟失數(shù)據(jù),那么 RDB 不適合你。 雖然 Redis 允許你設(shè)置不同的保存點(save point)來控制保存 RDB 文件的頻率, 但是, 因為RDB 文件需要保存整個數(shù)據(jù)集的狀態(tài), 所以它并不是一個輕松的操作。 因此你可能會至少 5 分鐘才保存一次 RDB 文件。 在這種情況下, 一旦發(fā)生故障停機,你就可能會丟失好幾分鐘的數(shù)據(jù)。
- 每次保存 RDB 的時候,Redis 都要?fork()?出一個子進程,并由子進程來進行實際的持久化工作。在數(shù)據(jù)集比較龐大時,?fork()可能會非常耗時,造成服務(wù)器在某某毫秒內(nèi)停止處理客戶端;? 如果數(shù)據(jù)集非常巨大,并且 CPU 時間非常緊張的話,那么這種停止時間甚至可能會長達整整一秒。雖然 AOF 重寫也需要進行?fork()?,但無論 AOF 重寫的執(zhí)行間隔有多長,數(shù)據(jù)的耐久性都不會有任何損失。
AOF 的優(yōu)點
- 使用 AOF 持久化會讓 Redis 變得非常耐久(much more durable):你可以設(shè)置不同的?fsync?(刷新操作)策略,比如無?fsync?,每秒鐘一次?fsync?,或者每次執(zhí)行寫入命令時?fsync?。 AOF 的默認(rèn)策略為每秒鐘?fsync?一次,在這種配置下,Redis 仍然可以保持良好的性能,并且就算發(fā)生故障停機,也最多只會丟失一秒鐘的數(shù)據(jù)(?fsync?會在后臺線程執(zhí)行,所以主線程可以繼續(xù)努力地處理命令請求)。
- AOF 文件是一個只進行追加操作的日志文件(append only log),因此對 AOF 文件的寫入不需要進行?seek?, 即使日志因為某些原因而包含了未寫入完整的命令(比如寫入時磁盤已滿,寫入中途停機,等等),?redis-check-aof?工具也可以輕易地修復(fù)這種問題。
重寫機制:
Redis 可以在 AOF 文件體積變得過大時,自動地在后臺對 AOF 進行重寫:重寫后的新 AOF 文件包含了恢復(fù)當(dāng)前數(shù)據(jù)集所需的最小命令集合。AOF 的方式也同時帶來了另一個問題。持久化文件會變的越來越大。例如我們調(diào)用incr test命令100次,文件中必須保存全部的100條命令,其實有99條都是多余的。因為要恢復(fù)數(shù)據(jù)庫的狀態(tài)其實文件中保存一條set test 100就夠了。
為了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照類似的方式將內(nèi)存中的數(shù)據(jù)以命令的方式保存到臨時文件中,最后替換原來的文件。具體過程如下
- redis調(diào)用fork ,現(xiàn)在有父子兩個進程
- 子進程根據(jù)內(nèi)存中的數(shù)據(jù)庫快照,往臨時文件中寫入重建數(shù)據(jù)庫狀態(tài)的命令
- 父進程繼續(xù)處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進程重寫失敗的話并不會出問題。
- 當(dāng)子進程把快照內(nèi)容寫入已命令方式寫到臨時文件中后,子進程發(fā)信號通知父進程。然后父進程把緩存的寫命令也寫入到臨時文件。
- 現(xiàn)在父進程可以使用臨時文件替換老的aof文件,并重命名,后面收到的寫命令也開始往新的aof文件中追加。
- ?AOF 文件有序地保存了對數(shù)據(jù)庫執(zhí)行的所有寫入操作, 這些寫入操作以 Redis 協(xié)議的格式保存, 因此 AOF 文件的內(nèi)容非常容易被人讀懂, 對文件進行分析(parse)也很輕松。 導(dǎo)出(export) AOF 文件也非常簡單: 舉個例子, 如果你不小心執(zhí)行了?FLUSHALL?命令, 但只要 AOF 文件未被重寫, 那么只要停止服務(wù)器, 移除 AOF 文件末尾的?FLUSHALL?命令, 并重啟 Redis , 就可以將數(shù)據(jù)集恢復(fù)到?FLUSHALL?執(zhí)行之前的狀態(tài)。
AOF 的缺點
- 對于相同的數(shù)據(jù)集來說,AOF 文件的體積通常要大于 RDB 文件的體積。
- 根據(jù)所使用的?fsync?策略,AOF 的速度可能會慢于 RDB 。
- 在一般情況下, 每秒?fsync?的性能依然非常高, 而關(guān)閉?fsync?可以讓 AOF 的速度和 RDB 一樣快, 即使在高負(fù)荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間(latency)。
- AOF 在過去曾經(jīng)發(fā)生過這樣的 bug :因為個別命令的原因,導(dǎo)致 AOF 文件在重新載入時,無法將數(shù)據(jù)集恢復(fù)成保存時的原樣。 (舉個例子,阻塞命令?BRPOPLPUSH?就曾經(jīng)引起過這樣的 bug 。) 測試套件里為這種情況添加了測試: 它們會自動生成隨機的、復(fù)雜的數(shù)據(jù)集, 并通過重新載入這些數(shù)據(jù)來確保一切正常。 雖然這種 bug 在 AOF 文件中并不常見, 但是對比來說, RDB 幾乎是不可能出現(xiàn)這種 bug 的。
RDB 和 AOF ,我應(yīng)該用哪一個?
一般來說,如果想達到足以媲美 PostgreSQL 的數(shù)據(jù)安全性,你應(yīng)該同時使用兩種持久化功能。
如果你非常關(guān)心你的數(shù)據(jù),但仍然可以承受數(shù)分鐘以內(nèi)的數(shù)據(jù)丟失,那么你可以只使用 RDB 持久化。
有很多用戶都只使用 AOF 持久化,但我們并不推薦這種方式:因為定時生成 RDB 快照(snapshot)非常便于進行數(shù)據(jù)庫備份,并且 RDB 恢復(fù)數(shù)據(jù)集的速度也要比 AOF 恢復(fù)的速度要快,除此之外,使用 RDB 還可以避免之前提到的 AOF 程序的 bug 。
因為以上提到的種種原因,未來我們可能會將 AOF 和 RDB 整合成單個持久化模型。
接下來的幾個小節(jié)將介紹 RDB 和 AOF 的更多細節(jié)。
RDB 快照
在默認(rèn)情況下, Redis 將數(shù)據(jù)庫快照保存在名字為?dump.rdb?的二進制文件中。
你可以對 Redis 進行設(shè)置,讓它在“?N?秒內(nèi)數(shù)據(jù)集至少有?M?個改動”這一條件被滿足時,自動保存一次數(shù)據(jù)集。
你也可以通過調(diào)用?SAVE?或者?BGSAVE?,手動讓 Redis 進行數(shù)據(jù)集保存操作。
比如說,以下設(shè)置會讓 Redis 在滿足“?60?秒內(nèi)有至少有?1000?個鍵被改動”這一條件時,自動保存一次數(shù)據(jù)集:
save 60 1000
這種持久化方式被稱為快照(snapshot)。
快照的運作方式(創(chuàng)造快照過程)
當(dāng) Redis 需要保存?dump.rdb?文件時,服務(wù)器執(zhí)行以下操作:
這種工作方式使得 Redis 可以從寫時復(fù)制(copy-on-write)機制中獲益。
只進行追加操作的文件(append-only file,AOF)
快照功能并不是非常耐久(durable):如果 Redis 因為某些原因而造成故障停機,那么服務(wù)器將丟失最近寫入、且仍未保存到快照中的那些數(shù)據(jù)。
盡管對于某些程序來說,數(shù)據(jù)的耐久性并不是最重要的考慮因素,但是對于那些追求完全耐久能力(full durability)的程序來說,快照功能就不太適用了。
從 1.1 版本開始, Redis 增加了一種完全耐久的持久化方式: AOF 持久化。
你可以通過修改配置文件來打開 AOF 功能:
appendonly yes
從現(xiàn)在開始,每當(dāng) Redis 執(zhí)行一個改變數(shù)據(jù)集的命令時(比如?SET),這個命令就會被追加到 AOF 文件的末尾。
這樣的話,當(dāng) Redis 重新啟時,程序就可以通過重新執(zhí)行 AOF 文件中的命令來達到重建數(shù)據(jù)集的目的。
AOF 重寫
因為 AOF 的運作方式是不斷地將命令追加到文件的末尾,所以隨著寫入命令的不斷增加, AOF 文件的體積也會變得越來越大。
舉個例子,如果你對一個計數(shù)器調(diào)用了 100 次?INCR?,那么僅僅是為了保存這個計數(shù)器的當(dāng)前值, AOF 文件就需要使用 100 條記錄(entry)。
然而在實際上,只使用一條?SET?命令已經(jīng)足以保存計數(shù)器的當(dāng)前值了,其余 99 條記錄實際上都是多余的。
為了處理這種情況, Redis 支持一種有趣的特性:可以在不打斷服務(wù)客戶端的情況下,對 AOF 文件進行重寫(rebuild)(下面的運行方式有)。
執(zhí)行?BGREWRITEAOF?命令,Redis 將生成一個新的 AOF 文件,這個文件包含重建當(dāng)前數(shù)據(jù)集所需的最少命令。
Redis 2.2 需要自己手動執(zhí)行?BGREWRITEAOF?命令; Redis 2.4 則可以自動觸發(fā) AOF 重寫,具體信息請查看 2.4 的示例配置文件。
AOF 有多耐久?
你可以配置 Redis 多久才將數(shù)據(jù)?fsync?到磁盤一次。
有三個選項:
- 每次有新命令追加到 AOF 文件時就執(zhí)行一次?fsync?:非常慢,也非常安全。(主動)
- 每秒?fsync?一次:足夠快(和使用 RDB 持久化差不多),并且在故障時只會丟失 1 秒鐘的數(shù)據(jù)。(默認(rèn))
- 從不?fsync?:將數(shù)據(jù)交給操作系統(tǒng)來處理。更快,也更不安全的選擇。(被動)
推薦(并且也是默認(rèn))的措施為每秒?fsync?一次,這種?fsync?策略可以兼顧速度和安全性。
總是?fsync?的策略在實際使用中非常慢,即使在 Redis 2.0 對相關(guān)的程序進行了改進之后仍是如此 —— 頻繁調(diào)用?fsync?注定了這種策略不可能快得起來。
如果 AOF 文件出錯了,怎么辦?
服務(wù)器可能在程序正在對 AOF 文件進行寫入時停機,如果停機造成了 AOF 文件出錯(corrupt),那么 Redis 在重啟時會拒絕載入這個 AOF 文件,從而確保數(shù)據(jù)的一致性不會被破壞。
當(dāng)發(fā)生這種情況時,可以用以下方法來修復(fù)出錯的 AOF 文件:
$ redis-check-aof --fix
AOF 的運作方式(重寫過程)
新舊兩個文件所保存的數(shù)據(jù)庫狀態(tài)是相同的,但是新的AOF文件不會包含任何浪費空間的冗余命令,通常體積會較舊AOF文件小很多。
AOF 重寫和 RDB 創(chuàng)建快照一樣,都巧妙地利用了寫時復(fù)制機制。
以下是 AOF 重寫的執(zhí)行步驟:
怎么從 RDB 持久化切換到 AOF 持久化
在 Redis 2.2 或以上版本,可以在不重啟的情況下,從 RDB 切換到 AOF :
redis-cli> CONFIG SET appendonly yes
?
redis-cli> CONFIG SET save ""
步驟 3 執(zhí)行的第一條命令開啟了 AOF 功能(appendonly yes): Redis 會阻塞直到初始 AOF 文件創(chuàng)建完成為止,之后 Redis 會繼續(xù)處理命令請求,并開始將寫入命令追加到 AOF 文件末尾。
步驟 3 執(zhí)行的第二條命令用于關(guān)閉 RDB 功能。這一步是可選的,如果你愿意的話,也可以同時使用 RDB 和 AOF 這兩種持久化功能。
別忘了在?redis.conf?中打開 AOF 功能!(配置文件)否則的話,服務(wù)器重啟之后,之前通過?CONFIG?SET?設(shè)置的配置就會被遺忘,程序會按原來的配置來啟動服務(wù)器。
譯注:原文這里還有介紹 2.0 版本的切換方式,考慮到 2.0 已經(jīng)很老舊了,這里省略了對那部分文檔的翻譯,有需要的請參考原文。
RDB 和 AOF 之間的相互作用
在版本號大于等于 2.4 的 Redis 中,?BGSAVE?執(zhí)行的過程中,不可以執(zhí)行?BGREWRITEAOF?。反過來說,在?BGREWRITEAOF?執(zhí)行的過程中,也不可以執(zhí)行?BGSAVE?。
這可以防止兩個 Redis 后臺進程同時對磁盤進行大量的 I/O 操作。
如果?BGSAVE?正在執(zhí)行,并且用戶顯示地調(diào)用?BGREWRITEAOF?命令,那么服務(wù)器將向用戶回復(fù)一個?OK?狀態(tài),并告知用戶,BGREWRITEAOF?已經(jīng)被預(yù)定執(zhí)行:一旦?BGSAVE?執(zhí)行完畢,?BGREWRITEAOF?就會正式開始。
當(dāng) Redis 啟動時,如果 RDB 持久化和 AOF 持久化都被打開了,那么程序會優(yōu)先使用 AOF 文件來恢復(fù)數(shù)據(jù)集,因為 AOF 文件所保存的數(shù)據(jù)通常是最完整的。
備份 Redis 數(shù)據(jù)
在閱讀這個小節(jié)前,先將下面這句話銘記于心:一定要備份你的數(shù)據(jù)庫!
磁盤故障,節(jié)點失效,諸如此類的問題都可能讓你的數(shù)據(jù)消失不見,不進行備份是非常危險的。
Redis 對于數(shù)據(jù)備份是非常友好的,因為你可以在服務(wù)器運行的時候?qū)?RDB 文件進行復(fù)制: RDB 文件一旦被創(chuàng)建,就不會進行任何修改。當(dāng)服務(wù)器要創(chuàng)建一個新的 RDB 文件時,它先將文件的內(nèi)容保存在一個臨時文件里面,當(dāng)臨時文件寫入完畢時,程序才使用?rename(2)?原子地用臨時文件替換原來的 RDB 文件。
這也就是說,無論何時,復(fù)制 RDB 文件都是絕對安全的。
以下是我們的建議:
- 創(chuàng)建一個定期任務(wù)(cron job), 每小時將一個 RDB 文件備份到一個文件夾, 并且每天將一個 RDB 文件備份到另一個文件夾。
- 確保快照的備份都帶有相應(yīng)的日期和時間信息, 每次執(zhí)行定期任務(wù)腳本時, 使用?find?命令來刪除過期的快照: 比如說, 你可以保留最近 48 小時內(nèi)的每小時快照, 還可以保留最近一兩個月的每日快照。
- 至少每天一次, 將 RDB 備份到你的數(shù)據(jù)中心之外, 或者至少是備份到你運行 Redis 服務(wù)器的物理機器之外。
容災(zāi)備份
Redis 的容災(zāi)備份基本上就是對數(shù)據(jù)進行備份,并將這些備份傳送到多個不同的外部數(shù)據(jù)中心。
容災(zāi)備份可以在 Redis 運行并產(chǎn)生快照的主數(shù)據(jù)中心發(fā)生嚴(yán)重的問題時,仍然讓數(shù)據(jù)處于安全狀態(tài)。
因為很多 Redis 用戶都是創(chuàng)業(yè)者,他們沒有大把大把的錢可以浪費,所以下面介紹的都是一些實用又便宜的容債備份方法:
- Amazon S3 ,以及其他類似 S3 的服務(wù),是一個構(gòu)建災(zāi)難備份系統(tǒng)的好地方。 最簡單的方法就是將你的每小時或者每日 RDB 備份加密并傳送到 S3 。 對數(shù)據(jù)的加密可以通過?gpg?-c?命令來完成(對稱加密模式)。 記得把你的密碼放到幾個不同的、安全的地方去(比如你可以把密碼復(fù)制給你組織里最重要的人物)。 同時使用多個儲存服務(wù)來保存數(shù)據(jù)文件,可以提升數(shù)據(jù)的安全性。
- 傳送快照可以使用 SCP 來完成(SSH 的組件)。 以下是簡單并且安全的傳送方法: 買一個離你的數(shù)據(jù)中心非常遠的 VPS , 裝上 SSH , 創(chuàng)建一個無口令的 SSH 客戶端 key , 并將這個 key 添加到 VPS 的 authorized_keys 文件中, 這樣就可以向這個 VPS 傳送快照備份文件了。 為了達到最好的數(shù)據(jù)安全性,至少要從兩個不同的提供商那里各購買一個 VPS 來進行數(shù)據(jù)容災(zāi)備份。
需要注意的是,這類容災(zāi)系統(tǒng)如果沒有小心地進行處理的話,是很容易失效的。
最低限度下,你應(yīng)該在文件傳送完畢之后,檢查所傳送備份文件的體積和原始快照文件的體積是否相同。如果你使用的是 VPS ,那么還可以通過比對文件的 SHA1 校驗和來確認(rèn)文件是否傳送完整。
另外,你還需要一個獨立的警報系統(tǒng),讓它在負(fù)責(zé)傳送備份文件的傳送器(transfer)失靈時通知你。
總結(jié)
以上是生活随笔為你收集整理的Redis持久化:AOF和RDB的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 海量数据的处理
- 下一篇: Redis:内存满了的解决方案