Redis 有兩種持久化的方式: 快照 (RDB文件) 和追加式文件 (AOF文件):
- RDB 持久化方式會在一個特定的間隔保存那個時間點的一個數據快照.
- AOF 持久化方式則會記錄每一個服務器收到的寫操作. 在服務啓動時, 這些記錄的操作會逐條執行從而重建出原來的數據. 寫操作命令記錄的格式跟 Redis 協議一致, 以追加的方式進行保存.
- Redis 的持久化是可以禁用的, 就是說你可以讓數據的生命週期只存在於服務器的運行時間裏.
- 兩種方式的持久化是可以同時存在的, 但是當 Redis 重啓時, AOF文件會被優先用於重建數據.
RDB
工作原理
- Redis 調用
fork()
, 產生一個子進程. - 子進程把數據寫到一個臨時的 RDB 文件.
- 當子進程寫完新的 RDB 文件後, 把舊的 RDB 文件替換掉.
文件路徑和名稱
默認 Redis 會把快照文件存儲爲當前目錄下一個名爲 dump.rdb
的文件. 要修改文件的存儲路徑和名稱, 可以通過修改配置文件 redis.conf
實現:
# RDB文件名,默認爲dump.rdb。
dbfilename dump.rdb
# 文件存放的目錄,AOF文件同樣存放在此目錄下。默認爲當前工作目錄。
dir ./
保存點 (RDB的啓用和禁用)
你可以配置保存點, 使 Redis 如果在每 N 秒後數據發生了 M 次改變就保存快照文件. 例如下面這個保存點配置表示每 60 秒, 如果數據發生了 1000 次以上的變動, Redis就會自動保存快照文件:
save 60 1000
保存點可以設置多個, Redis 的配置文件就默認設置了 3 個保存點:
# 格式爲:save <seconds> <changes>
# 可以設置多個。
save 900 1 #900秒後至少1個key有變動
save 300 10 #300秒後至少10個key有變動
save 60 10000 #60秒後至少10000個key有變動
如果想禁用快照保存的功能, 可以通過註釋掉所有 "save" 配置達到,或者在最後一條 "save" 配置後添加如下的配置:
save ""
錯誤處理
默認情況下, 如果 Redis 在後臺生成快照的時候失敗, 那麼就會停止接收數據, 目的是讓用戶能知道數據沒有持久化成功.
但是如果你有其他的方式可以監控到 Redis 及其持久化的狀態, 那麼可以把這個功能禁止掉.
stop-writes-on-bgsave-error yes
數據壓縮
默認 Redis 會採用 LZF
對數據進行壓縮.
如果你想節省點 CPU 的性能, 你可以把壓縮功能禁用掉, 但是數據集就會比沒壓縮的時候要大.
rdbcompression yes
數據校驗
從版本 5 的 RDB 的開始, 一個 CRC64 的校驗碼會放在文件的末尾. 這樣更能保證文件的完整性, 但是在保存或者加載文件時會損失一定的性能 (大概10%).
如果想追求更高的性能, 可以把它禁用掉, 這樣文件在寫入校驗碼時會用 0 替代, 加載的時候看到 0 就會直接跳過校驗.
rdbchecksum yes
手動生成快照
Redis 提供了兩個命令用於手動生成快照.
BGSAVE
BGSAVE
命令使用後臺的方式保存 RDB 文件, 調用此命令後, 會立刻返回OK
.
Redis 會產生一個子進程進行快照寫入硬盤. 父進程繼續處理命令請求.
SAVE
SAVE
命令會使用同步的方式生成 RDB 快照文件, 這意味着在這個過程中會阻塞所有其他客戶端的請求.
因此不建議在生產環境使用這個命令.
重點
- 如果用戶設置了
save 60 1000
, 那麼從 Redis 最近一次創建快照之後開始算起. 當條件被滿足時, 就會自動出發BGSAVE
命令. 如果有多個save
配置, 那麼當任意一個被滿足時, 都會出發一次BGSAVE
命令. - 當 Redis 通過
SHUTDOWN
命令, 來關閉服務時, 或者接收到標準TERM
信號時, 會執行一次SAVE
命令, 阻塞所有客戶端, 並且執行完畢後會關閉 Redis 服務.
這裏注意的是 fork
操作會阻塞, 導致 Redis 讀寫性能下降. 我們可以控制單個 Redis 實例的最大內存, 來儘可能降低 Redis 在 fork
時的事件消耗. 以及上面提到的自動觸發的頻率減少 fork
次數, 或者使用手動觸發, 根據自己的機制來完成持久化.
AOF
快照並不是很可靠. 如果你的電腦突然宕機了, 或者電源斷了, 又或者不小心殺掉了進程, 那麼最新的數據就會丟失.
而 AOF 文件則提供了一種更爲可靠的持久化方式. 每當 Redis 接受到會修改數據集的命令時, 就會把命令追加到 AOF 文件裏, 當你重啓 Redis 時, AOF 裏的命令會被重新執行一次, 重建數據.
啓用 AOF
把配置項 appendonly
設爲 yes
:
appendonly yes
文件路徑和名稱
# 文件存放目錄,與RDB共用。默認爲當前工作目錄。
dir ./
# 默認文件名爲appendonly.aof
appendfilename "appendonly.aof"
可靠性
你可以配置 Redis 調用 fsync
的頻率, 有三個選項:
- 每當有新命令追加到 AOF 的時候調用
fsync
. 速度最慢, 最安全. - 每秒
fsync
一次. 速度快 (2.4版本跟快照方式速度差不多), 安全性不錯 (最多丟失 1 秒的數據). - 從不
fsync
, 交由系統去處理. 這個方式速度最快, 但是安全性一般.
推薦使用每秒 fsync 一次的方式 (默認的方式), 因爲它速度快, 安全性也不錯. 相關配置如下:
# appendfsync always
appendfsync everysec
# appendfsync no
對於增量追加到文件這一步主要的流程是: 命令寫入=》追加到 aof_buf =》同步到aof磁盤. 那麼這裏爲什麼要先寫入 buf 在同步到磁盤呢? 如果實時寫入磁盤會帶來非常高的磁盤IO, 影響整體性能.
AOF 重寫是爲了減少 AOF 文件的大小, 隨着寫操作的不斷增加, AOF 文件會越來越大. 例如你遞增一個計數器 100 次, 那麼最終結果就是數據集裏的計數器的值爲最終的遞增結果, 但是 AOF 文件裏卻會把這 100 次操作完整的記錄下來.
而事實上要恢復這個記錄, 只需要 1 個命令就行了, 也就是說 AOF 文件裏那 100 條命令其實可以精簡爲 1 條. 所以 Redis 支持這樣一個功能: 在不中斷服務的情況下在後臺重建 AOF 文件.
對於上圖有四個關鍵點補充一下:
- 在重寫期間, 由於主進程依然在響應命令, 爲了保證最終備份的完整性; 因此它依然會寫入舊的 AOF file 中, 如果重寫失敗, 能夠保證數據不丟失.
- 爲了把重寫期間響應的寫入信息也寫入到新的文件中, 因此也會爲子進程保留一個 buf, 防止新寫的 file 丟失數據.
- 重寫是直接把當前內存的數據生成對應命令, 並不需要讀取老的 AOF 文件進行分析、命令合併.
- AOF 文件直接採用的文本協議, 主要是兼容性好、追加方便、可讀性高可認爲修改修復.
性能與實踐
通過上面的分析, 我們都知道 RDB 的快照、AOF的重寫都需要 fork
, 這是一個重量級操作, 會對 Redis 造成阻塞. 因此爲了不影響 Redis 主進程響應, 我們需要儘可能降低阻塞.
- 降低
fork
的頻率, 比如可以手動來觸發 RDB 生成快照、與AOF重寫; - 控制 Redis 最大使用內存, 防止
fork
耗時過長; - 使用更牛逼的硬件;
- 合理配置 Linux 的內存分配策略, 避免因爲物理內存不足導致
fork
失敗.
在線上我們到底該怎麼做? 我提供一些自己的實踐經驗.
- 如果 Redis 中的數據並不是特別敏感或者可以通過其它方式重寫生成數據, 可以關閉持久化;
- 自己制定策略定期檢查 Redis 的情況, 然後可以手動觸發備份、重寫數據;
- 單機如果部署多個實例, 要防止多個機器同時運行持久化、重寫操作, 防止出現內存、CPU、IO資源競爭, 讓持久化變爲串行;
- 可以加入主從機器, 利用一臺從機器進行備份處理, 其它機器正常響應客戶端的命令;
- RDB 持久化與 AOF 持久化可以同時存在, 配合使用.