# 持久化(persistence)
Note
本文檔翻譯自 [http://redis.io/topics/persistence](http://redis.io/topics/persistence) 。
這篇文章提供了 Redis 持久化的技術性描述, 推薦所有 Redis 用戶閱讀。
要更廣泛地了解 Redis 持久化, 以及這種持久化所保證的耐久性(durability), 請參考文章 [Redis persistence demystified](http://oldblog.antirez.com/post/redis-persistence-demystified.html) ([中文](http://blog.nosqlfan.com/html/3813.html))。
## Redis 持久化
Redis 提供了多種不同級別的持久化方式:
* RDB 持久化可以在指定的時間間隔內生成數據集的時間點快照(point-in-time snapshot)。
* AOF 持久化記錄服務器執行的所有寫操作命令,并在服務器啟動時,通過重新執行這些命令來還原數據集。 AOF 文件中的命令全部以 Redis 協議的格式來保存,新命令會被追加到文件的末尾。 Redis 還可以在后臺對 AOF 文件進行重寫(rewrite),使得 AOF 文件的體積不會超出保存數據集狀態所需的實際大小。
* Redis 還可以同時使用 AOF 持久化和 RDB 持久化。 在這種情況下, 當 Redis 重啟時, 它會優先使用 AOF 文件來還原數據集, 因為 AOF 文件保存的數據集通常比 RDB 文件所保存的數據集更完整。
* 你甚至可以關閉持久化功能,讓數據只在服務器運行時存在。
了解 RDB 持久化和 AOF 持久化之間的異同是非常重要的, 以下幾個小節將詳細地介紹這這兩種持久化功能, 并對它們的相同和不同之處進行說明。
## RDB 的優點
* RDB 是一個非常緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數據集。 這種文件非常適合用于進行備份: 比如說,你可以在最近的 24 小時內,每小時備份一次 RDB 文件,并且在每個月的每一天,也備份一個 RDB 文件。 這樣的話,即使遇上問題,也可以隨時將數據集還原到不同的版本。
* RDB 非常適用于災難恢復(disaster recovery):它只有一個文件,并且內容都非常緊湊,可以(在加密后)將它傳送到別的數據中心,或者亞馬遜 S3 中。
* RDB 可以最大化 Redis 的性能:父進程在保存 RDB 文件時唯一要做的就是 `fork` 出一個子進程,然后這個子進程就會處理接下來的所有保存工作,父進程無須執行任何磁盤 I/O 操作。
* RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。
## RDB 的缺點
* 如果你需要盡量避免在服務器故障時丟失數據,那么 RDB 不適合你。 雖然 Redis 允許你設置不同的保存點(save point)來控制保存 RDB 文件的頻率, 但是, 因為RDB 文件需要保存整個數據集的狀態, 所以它并不是一個輕松的操作。 因此你可能會至少 5 分鐘才保存一次 RDB 文件。 在這種情況下, 一旦發生故障停機, 你就可能會丟失好幾分鐘的數據。
* 每次保存 RDB 的時候,Redis 都要 `fork()` 出一個子進程,并由子進程來進行實際的持久化工作。 在數據集比較龐大時, `fork()` 可能會非常耗時,造成服務器在某某毫秒內停止處理客戶端; 如果數據集非常巨大,并且 CPU 時間非常緊張的話,那么這種停止時間甚至可能會長達整整一秒。 雖然 AOF 重寫也需要進行 `fork()` ,但無論 AOF 重寫的執行間隔有多長,數據的耐久性都不會有任何損失。
## AOF 的優點
* 使用 AOF 持久化會讓 Redis 變得非常耐久(much more durable):你可以設置不同的 `fsync` 策略,比如無 `fsync` ,每秒鐘一次 `fsync` ,或者每次執行寫入命令時 `fsync` 。 AOF 的默認策略為每秒鐘 `fsync` 一次,在這種配置下,Redis 仍然可以保持良好的性能,并且就算發生故障停機,也最多只會丟失一秒鐘的數據( `fsync` 會在后臺線程執行,所以主線程可以繼續努力地處理命令請求)。
* AOF 文件是一個只進行追加操作的日志文件(append only log), 因此對 AOF 文件的寫入不需要進行 `seek` , 即使日志因為某些原因而包含了未寫入完整的命令(比如寫入時磁盤已滿,寫入中途停機,等等), `redis-check-aof` 工具也可以輕易地修復這種問題。
* Redis 可以在 AOF 文件體積變得過大時,自動地在后臺對 AOF 進行重寫: 重寫后的新 AOF 文件包含了恢復當前數據集所需的最小命令集合。 整個重寫操作是絕對安全的,因為 Redis 在創建新 AOF 文件的過程中,會繼續將命令追加到現有的 AOF 文件里面,即使重寫過程中發生停機,現有的 AOF 文件也不會丟失。 而一旦新 AOF 文件創建完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,并開始對新 AOF 文件進行追加操作。
* AOF 文件有序地保存了對數據庫執行的所有寫入操作, 這些寫入操作以 Redis 協議的格式保存, 因此 AOF 文件的內容非常容易被人讀懂, 對文件進行分析(parse)也很輕松。 導出(export) AOF 文件也非常簡單: 舉個例子, 如果你不小心執行了 [FLUSHALL](../server/flushall.html#flushall) 命令, 但只要 AOF 文件未被重寫, 那么只要停止服務器, 移除 AOF 文件末尾的 [FLUSHALL](../server/flushall.html#flushall) 命令, 并重啟 Redis , 就可以將數據集恢復到 [FLUSHALL](../server/flushall.html#flushall) 執行之前的狀態。
## AOF 的缺點
* 對于相同的數據集來說,AOF 文件的體積通常要大于 RDB 文件的體積。
* 根據所使用的 `fsync` 策略,AOF 的速度可能會慢于 RDB 。 在一般情況下, 每秒 `fsync` 的性能依然非常高, 而關閉 `fsync` 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間(latency)。
* AOF 在過去曾經發生過這樣的 bug : 因為個別命令的原因,導致 AOF 文件在重新載入時,無法將數據集恢復成保存時的原樣。 (舉個例子,阻塞命令 [BRPOPLPUSH](../list/brpoplpush.html#brpoplpush) 就曾經引起過這樣的 bug 。) 測試套件里為這種情況添加了測試: 它們會自動生成隨機的、復雜的數據集, 并通過重新載入這些數據來確保一切正常。 雖然這種 bug 在 AOF 文件中并不常見, 但是對比來說, RDB 幾乎是不可能出現這種 bug 的。
## RDB 和 AOF ,我應該用哪一個?
一般來說, 如果想達到足以媲美 PostgreSQL 的數據安全性, 你應該同時使用兩種持久化功能。
如果你非常關心你的數據, 但仍然可以承受數分鐘以內的數據丟失, 那么你可以只使用 RDB 持久化。
有很多用戶都只使用 AOF 持久化, 但我們并不推薦這種方式: 因為定時生成 RDB 快照(snapshot)非常便于進行數據庫備份, 并且 RDB 恢復數據集的速度也要比 AOF 恢復的速度要快, 除此之外, 使用 RDB 還可以避免之前提到的 AOF 程序的 bug 。
Note
因為以上提到的種種原因, 未來我們可能會將 AOF 和 RDB 整合成單個持久化模型。 (這是一個長期計劃。)
接下來的幾個小節將介紹 RDB 和 AOF 的更多細節。
## RDB 快照
在默認情況下, Redis 將數據庫快照保存在名字為 `dump.rdb` 的二進制文件中。
你可以對 Redis 進行設置, 讓它在“ `N` 秒內數據集至少有 `M` 個改動”這一條件被滿足時, 自動保存一次數據集。
你也可以通過調用 [SAVE](../server/save.html#save) 或者 [BGSAVE](../server/bgsave.html#bgsave) , 手動讓 Redis 進行數據集保存操作。
比如說, 以下設置會讓 Redis 在滿足“ `60` 秒內有至少有 `1000` 個鍵被改動”這一條件時, 自動保存一次數據集:
```
save 60 1000
```
這種持久化方式被稱為快照(snapshot)。
## 快照的運作方式
當 Redis 需要保存 `dump.rdb` 文件時, 服務器執行以下操作:
1. Redis 調用 `fork()` ,同時擁有父進程和子進程。
2. 子進程將數據集寫入到一個臨時 RDB 文件中。
3. 當子進程完成對新 RDB 文件的寫入時,Redis 用新 RDB 文件替換原來的 RDB 文件,并刪除舊的 RDB 文件。
這種工作方式使得 Redis 可以從寫時復制(copy-on-write)機制中獲益。
## 只進行追加操作的文件(append-only file,AOF)
快照功能并不是非常耐久(durable): 如果 Redis 因為某些原因而造成故障停機, 那么服務器將丟失最近寫入、且仍未保存到快照中的那些數據。
盡管對于某些程序來說, 數據的耐久性并不是最重要的考慮因素, 但是對于那些追求完全耐久能力(full durability)的程序來說, 快照功能就不太適用了。
從 1.1 版本開始, Redis 增加了一種完全耐久的持久化方式: AOF 持久化。
你可以通過修改配置文件來打開 AOF 功能:
```
appendonly yes
```
從現在開始, 每當 Redis 執行一個改變數據集的命令時(比如 [SET](../string/set.html#set)), 這個命令就會被追加到 AOF 文件的末尾。
這樣的話, 當 Redis 重新啟時, 程序就可以通過重新執行 AOF 文件中的命令來達到重建數據集的目的。
## AOF 重寫
因為 AOF 的運作方式是不斷地將命令追加到文件的末尾, 所以隨著寫入命令的不斷增加, AOF 文件的體積也會變得越來越大。
舉個例子, 如果你對一個計數器調用了 100 次 [INCR](../string/incr.html#incr) , 那么僅僅是為了保存這個計數器的當前值, AOF 文件就需要使用 100 條記錄(entry)。
然而在實際上, 只使用一條 [SET](../string/set.html#set) 命令已經足以保存計數器的當前值了, 其余 99 條記錄實際上都是多余的。
為了處理這種情況, Redis 支持一種有趣的特性: 可以在不打斷服務客戶端的情況下, 對 AOF 文件進行重建(rebuild)。
執行 [BGREWRITEAOF](../server/bgrewriteaof.html#bgrewriteaof) 命令, Redis 將生成一個新的 AOF 文件, 這個文件包含重建當前數據集所需的最少命令。
Redis 2.2 需要自己手動執行 [BGREWRITEAOF](../server/bgrewriteaof.html#bgrewriteaof) 命令; Redis 2.4 則可以自動觸發 AOF 重寫, 具體信息請查看 2.4 的示例配置文件。
## AOF 的耐久性如何?
你可以配置 Redis 多久才將數據 `fsync` 到磁盤一次。
有三個選項:
* 每次有新命令追加到 AOF 文件時就執行一次 `fsync` :非常慢,也非常安全。
* 每秒 `fsync` 一次:足夠快(和使用 RDB 持久化差不多),并且在故障時只會丟失 1 秒鐘的數據。
* 從不 `fsync` :將數據交給操作系統來處理。更快,也更不安全的選擇。
推薦(并且也是默認)的措施為每秒 `fsync` 一次, 這種 `fsync` 策略可以兼顧速度和安全性。
總是 `fsync` 的策略在實際使用中非常慢, 即使在 Redis 2.0 對相關的程序進行了改進之后仍是如此 —— 頻繁調用 `fsync` 注定了這種策略不可能快得起來。
## 如果 AOF 文件出錯了,怎么辦?
服務器可能在程序正在對 AOF 文件進行寫入時停機, 如果停機造成了 AOF 文件出錯(corrupt), 那么 Redis 在重啟時會拒絕載入這個 AOF 文件, 從而確保數據的一致性不會被破壞。
當發生這種情況時, 可以用以下方法來修復出錯的 AOF 文件:
1. 為現有的 AOF 文件創建一個備份。
2. 使用 Redis 附帶的 `redis-check-aof` 程序,對原來的 AOF 文件進行修復。
> ```
> $ redis-check-aof --fix
>
> ```
1. (可選)使用 `diff -u` 對比修復后的 AOF 文件和原始 AOF 文件的備份,查看兩個文件之間的不同之處。
2. 重啟 Redis 服務器,等待服務器載入修復后的 AOF 文件,并進行數據恢復。
## AOF 的運作方式
AOF 重寫和 RDB 創建快照一樣,都巧妙地利用了寫時復制機制。
以下是 AOF 重寫的執行步驟:
1. Redis 執行 `fork()` ,現在同時擁有父進程和子進程。
2. 子進程開始將新 AOF 文件的內容寫入到臨時文件。
3. 對于所有新執行的寫入命令,父進程一邊將它們累積到一個內存緩存中,一邊將這些改動追加到現有 AOF 文件的末尾: 這樣即使在重寫的中途發生停機,現有的 AOF 文件也還是安全的。
4. 當子進程完成重寫工作時,它給父進程發送一個信號,父進程在接收到信號之后,將內存緩存中的所有數據追加到新 AOF 文件的末尾。
5. 搞定!現在 Redis 原子地用新文件替換舊文件,之后所有命令都會直接追加到新 AOF 文件的末尾。
## 怎么從 RDB 持久化切換到 AOF 持久化
在 Redis 2.2 或以上版本,可以在不重啟的情況下,從 RDB 切換到 AOF :
1. 為最新的 `dump.rdb` 文件創建一個備份。
2. 將備份放到一個安全的地方。
3. 執行以下兩條命令:
> ```
> redis-cli> CONFIG SET appendonly yes
>
> redis-cli> CONFIG SET save ""
>
> ```
1. 確保命令執行之后,數據庫的鍵的數量沒有改變。
2. 確保寫命令會被正確地追加到 AOF 文件的末尾。
步驟 3 執行的第一條命令開啟了 AOF 功能: Redis 會阻塞直到初始 AOF 文件創建完成為止, 之后 Redis 會繼續處理命令請求, 并開始將寫入命令追加到 AOF 文件末尾。
步驟 3 執行的第二條命令用于關閉 RDB 功能。 這一步是可選的, 如果你愿意的話, 也可以同時使用 RDB 和 AOF 這兩種持久化功能。
Note
別忘了在 `redis.conf` 中打開 AOF 功能! 否則的話, 服務器重啟之后, 之前通過 `CONFIG SET` 設置的配置就會被遺忘, 程序會按原來的配置來啟動服務器。
Note
譯注: 原文這里還有介紹 2.0 版本的切換方式, 考慮到 2.0 已經很老舊了, 這里省略了對那部分文檔的翻譯, 有需要的請參考原文。
## RDB 和 AOF 之間的相互作用
在版本號大于等于 2.4 的 Redis 中, [BGSAVE](../server/bgsave.html#bgsave) 執行的過程中, 不可以執行 [BGREWRITEAOF](../server/bgrewriteaof.html#bgrewriteaof) 。 反過來說, 在 [BGREWRITEAOF](../server/bgrewriteaof.html#bgrewriteaof) 執行的過程中, 也不可以執行 [BGSAVE](../server/bgsave.html#bgsave) 。
這可以防止兩個 Redis 后臺進程同時對磁盤進行大量的 I/O 操作。
如果 [BGSAVE](../server/bgsave.html#bgsave) 正在執行, 并且用戶顯示地調用 [BGREWRITEAOF](../server/bgrewriteaof.html#bgrewriteaof) 命令, 那么服務器將向用戶回復一個 `OK` 狀態, 并告知用戶, [BGREWRITEAOF](../server/bgrewriteaof.html#bgrewriteaof) 已經被預定執行: 一旦 [BGSAVE](../server/bgsave.html#bgsave) 執行完畢, [BGREWRITEAOF](../server/bgrewriteaof.html#bgrewriteaof) 就會正式開始。
當 Redis 啟動時, 如果 RDB 持久化和 AOF 持久化都被打開了, 那么程序會優先使用 AOF 文件來恢復數據集, 因為 AOF 文件所保存的數據通常是最完整的。
## 備份 Redis 數據
在閱讀這個小節前, 先將下面這句話銘記于心: 一定要備份你的數據庫!
磁盤故障, 節點失效, 諸如此類的問題都可能讓你的數據消失不見, 不進行備份是非常危險的。
Redis 對于數據備份是非常友好的, 因為你可以在服務器運行的時候對 RDB 文件進行復制: RDB 文件一旦被創建, 就不會進行任何修改。 當服務器要創建一個新的 RDB 文件時, 它先將文件的內容保存在一個臨時文件里面, 當臨時文件寫入完畢時, 程序才使用 `rename(2)` 原子地用臨時文件替換原來的 RDB 文件。
這也就是說, 無論何時, 復制 RDB 文件都是絕對安全的。
以下是我們的建議:
* 創建一個定期任務(cron job), 每小時將一個 RDB 文件備份到一個文件夾, 并且每天將一個 RDB 文件備份到另一個文件夾。
* 確保快照的備份都帶有相應的日期和時間信息, 每次執行定期任務腳本時, 使用 `find` 命令來刪除過期的快照: 比如說, 你可以保留最近 48 小時內的每小時快照, 還可以保留最近一兩個月的每日快照。
* 至少每天一次, 將 RDB 備份到你的數據中心之外, 或者至少是備份到你運行 Redis 服務器的物理機器之外。
## 容災備份
Redis 的容災備份基本上就是對數據進行備份, 并將這些備份傳送到多個不同的外部數據中心。
容災備份可以在 Redis 運行并產生快照的主數據中心發生嚴重的問題時, 仍然讓數據處于安全狀態。
因為很多 Redis 用戶都是創業者, 他們沒有大把大把的錢可以浪費, 所以下面介紹的都是一些實用又便宜的容災備份方法:
* Amazon S3 ,以及其他類似 S3 的服務,是一個構建災難備份系統的好地方。 最簡單的方法就是將你的每小時或者每日 RDB 備份加密并傳送到 S3 。 對數據的加密可以通過 `gpg -c` 命令來完成(對稱加密模式)。 記得把你的密碼放到幾個不同的、安全的地方去(比如你可以把密碼復制給你組織里最重要的人物)。 同時使用多個儲存服務來保存數據文件,可以提升數據的安全性。
* 傳送快照可以使用 SCP 來完成(SSH 的組件)。 以下是簡單并且安全的傳送方法: 買一個離你的數據中心非常遠的 VPS , 裝上 SSH , 創建一個無口令的 SSH 客戶端 key , 并將這個 key 添加到 VPS 的 authorized_keys 文件中, 這樣就可以向這個 VPS 傳送快照備份文件了。 為了達到最好的數據安全性,至少要從兩個不同的提供商那里各購買一個 VPS 來進行數據容災備份。
需要注意的是, 這類容災系統如果沒有小心地進行處理的話, 是很容易失效的。
最低限度下, 你應該在文件傳送完畢之后, 檢查所傳送備份文件的體積和原始快照文件的體積是否相同。 如果你使用的是 VPS , 那么還可以通過比對文件的 SHA1 校驗和來確認文件是否傳送完整。
另外, 你還需要一個獨立的警報系統, 讓它在負責傳送備份文件的傳送器(transfer)失靈時通知你。
- Redis 文檔
- 鍵空間通知(keyspace notification)
- 事務(transaction)
- 發布與訂閱(pub/sub)
- 復制(Replication)
- 通信協議(protocol)
- 持久化(persistence)
- Sentinel
- 集群教程
- Redis 集群規范
- Redis 命令參考
- Key(鍵)
- DEL
- DUMP
- EXISTS
- EXPIRE
- EXPIREAT
- KEYS
- MIGRATE
- MOVE
- OBJECT
- PERSIST
- PEXPIRE
- PEXPIREAT
- PTTL
- RANDOMKEY
- RENAME
- RENAMENX
- RESTORE
- SORT
- TYPE
- SCAN
- String(字符串)
- APPEND
- BITCOUNT
- BITOP
- DECR
- DECRBY
- GET
- GETBIT
- GETRANGE
- GETSET
- INCR
- INCRBY
- INCRBYFLOAT
- MGET
- MSET
- MSETNX
- PSETEX
- SET
- SETBIT
- SETEX
- SETNX
- SETRANGE
- STRLEN
- Hash(哈希表)
- HDEL
- HEXISTS
- HGET
- HGETALL
- HINCRBY
- HINCRBYFLOAT
- HKEYS
- HLEN
- HMGET
- HMSET
- HSET
- HSETNX
- HVALS
- HSCAN
- List(列表)
- BLPOP
- BRPOP
- BRPOPLPUSH
- LINDEX
- LINSERT
- LLEN
- LPOP
- LPUSH
- LRANGE
- LREM
- LSET
- LTRIM
- RPOP
- RPOPLPUSH
- RPUSH
- RPUSHX
- Set(集合)
- SADD
- SCARD
- SDIFF
- SDIFFSTORE
- SINTER
- SINTER
- SINTERSTORE
- SISMEMBER
- SMEMBERS
- SMOVE
- SPOP
- SRANDMEMBER
- SREM
- SUNION
- SUNIONSTORE
- SSCAN
- SortedSet(有序集合)
- ZADD
- ZCARD
- ZCOUNT
- ZINCRBY
- ZRANGE
- ZRANGEBYSCORE
- ZRANK
- ZREM
- ZREMRANGEBYRANK
- ZREMRANGEBYSCORE
- ZREVRANGE
- ZREVRANGEBYSCORE
- ZREVRANK
- ZSCORE
- ZUNIONSTORE
- ZINTERSTORE
- ZSCAN
- Pub/Sub(發布/訂閱)
- PSUBSCRIBE
- PUBLISH
- PUBSUB
- PUNSUBSCRIBE
- SUBSCRIBE
- UNSUBSCRIBE
- Transaction(事務)
- DISCARD
- EXEC
- MULTI
- UNWATCH
- WATCH
- Script(腳本)
- EVAL
- EVALSHA
- SCRIPT EXISTS
- SCRIPT FLUSH
- SCRIPT KILL
- SCRIPT LOAD
- Connection(連接)
- AUTH
- ECHO
- PING
- QUIT
- SELECT
- Server(服務器)
- BGREWRITEAOF
- BGSAVE
- CLIENT GETNAME
- CLIENT KILL
- CLIENT LIST
- CLIENT SETNAME
- CONFIG GET
- CONFIG RESETSTAT
- CONFIG REWRITE
- CONFIG SET
- DBSIZE
- DEBUG OBJECT
- DEBUG SEGFAULT
- FLUSHALL
- FLUSHDB
- INFO
- LASTSAVE
- MONITOR
- PSYNC
- SAVE
- SHUTDOWN
- SLAVEOF
- SLOWLOG
- SYNC
- TIME
- 關于