Redis 持久化

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 快照文件, 這意味着在這個過程中會阻塞所有其他客戶端的請求.

因此不建議在生產環境使用這個命令.

重點

  1. 如果用戶設置了 save 60 1000, 那麼從 Redis 最近一次創建快照之後開始算起. 當條件被滿足時, 就會自動出發 BGSAVE 命令. 如果有多個 save 配置, 那麼當任意一個被滿足時, 都會出發一次 BGSAVE 命令.
  2. 當 Redis 通過 SHUTDOWN 命令, 來關閉服務時, 或者接收到標準 TERM 信號時, 會執行一次 SAVE 命令, 阻塞所有客戶端, 並且執行完畢後會關閉 Redis 服務.

clipboard.png

這裏注意的是 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 文件.

clipboard.png

對於上圖有四個關鍵點補充一下:

  • 在重寫期間, 由於主進程依然在響應命令, 爲了保證最終備份的完整性; 因此它依然會寫入舊的 AOF file 中, 如果重寫失敗, 能夠保證數據不丟失.
  • 爲了把重寫期間響應的寫入信息也寫入到新的文件中, 因此也會爲子進程保留一個 buf, 防止新寫的 file 丟失數據.
  • 重寫是直接把當前內存的數據生成對應命令, 並不需要讀取老的 AOF 文件進行分析、命令合併.
  • AOF 文件直接採用的文本協議, 主要是兼容性好、追加方便、可讀性高可認爲修改修復.

性能與實踐

通過上面的分析, 我們都知道 RDB 的快照、AOF的重寫都需要 fork, 這是一個重量級操作, 會對 Redis 造成阻塞. 因此爲了不影響 Redis 主進程響應, 我們需要儘可能降低阻塞.

  • 降低 fork 的頻率, 比如可以手動來觸發 RDB 生成快照、與AOF重寫;
  • 控制 Redis 最大使用內存, 防止 fork 耗時過長;
  • 使用更牛逼的硬件;
  • 合理配置 Linux 的內存分配策略, 避免因爲物理內存不足導致 fork 失敗.

在線上我們到底該怎麼做? 我提供一些自己的實踐經驗.

  • 如果 Redis 中的數據並不是特別敏感或者可以通過其它方式重寫生成數據, 可以關閉持久化;
  • 自己制定策略定期檢查 Redis 的情況, 然後可以手動觸發備份、重寫數據;
  • 單機如果部署多個實例, 要防止多個機器同時運行持久化、重寫操作, 防止出現內存、CPU、IO資源競爭, 讓持久化變爲串行;
  • 可以加入主從機器, 利用一臺從機器進行備份處理, 其它機器正常響應客戶端的命令;
  • RDB 持久化與 AOF 持久化可以同時存在, 配合使用.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章