Redis持久化背後的故事

Tu9oh0st發表於2019-07-23

Redis持久化

Redis提供了不同的持久化選項:

  • RDB持久化以指定的時間間隔儲存那個時間點的資料快照。
  • AOF持久化方法則會記錄每一個伺服器收到的寫操作。在伺服器啟動時,這些記錄的操作會逐條執行從而重建出原來的資料。寫操作命令記錄的格式跟Redis協議一致,以追加的方式進行儲存。
  • Redis的持久化時可以禁用的,就是說你可以讓資料的生命週期只存在伺服器的執行時間裡。
  • 兩種方式的持久化是可以同時存在的,但是當Redis重啟時,AOF檔案會被優先用於重建資料。

最重要的是要理解RDB和AOF持久化之間的不同區別。

RDB持久化

RDB檔案的建立和載入

有兩個Redis命令可以用於生成RDB檔案,一個是SAVE,另一個是BGSAVE

SAVE命令會阻塞Redis伺服器程式,直到RDB檔案建立完畢為止,在伺服器程式阻塞期間,伺服器不能處理任何命令請求:

Redis持久化背後的故事

SAVE命令直接阻塞伺服器程式的做法不同,BGSAVE命令會去派生出一個子程式,然後由紫禁城負責建立RDB檔案,伺服器程式(父程式)繼續處理命令請求:

Redis持久化背後的故事

RDB的優勢

  • RDB檔案是一個經過壓縮的二進位制檔案,儲存著某個時間點的Redis資料。RDB檔案非常適合備份。你可以設定一個時間點對RDB檔案進行歸檔,這樣就能在需要的時候很輕易的把資料恢復到不同的版本。
  • RDB非常適用於災備。單檔案很方便傳輸到遠端的伺服器上。
  • RDB的效能很好,需要進行持久化時,主程式會fork一個子程式出來,然後把持久化工作交給子程式,自己不會有相關的I/O操作,也就是上面說是的BGSAVE
  • 比起AOF,在資料量比較大的情況下,RDB的啟動速度更快。

RDB的缺點

  • RDB容易造成資料的丟失。假設每5分鐘儲存一次快照,如果Redis因為某些原因不能工作,那麼從上次產生快照到Redis出現問題這段時間的資料就會丟失了。上述的SAVE缺點。
  • RDB使用fork()產生子程式進行資料的持久化,如果資料比較大可能就會花費點時間,造成Redis停止服務幾毫秒。如果資料很大且CPU效能不是很好的時候,停止服務的時間甚至更多。上述的BGSAVE缺點。

檔案路徑和名稱

預設Redis會把快照檔案儲存為當前目錄下一個名為dump.rdb的檔案。要修改檔案的儲存路徑和名稱,可以通過修改配置檔案redis.conf實現:

# RDB檔名,預設為dump.rdb。
dbfilename dump.rdb

# 檔案存放的目錄,AOF檔案同樣存放在此目錄下。預設為當前工作目錄。
dir ./

Redis持久化背後的故事

儲存點(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

AOF持久化

快照並不是很可靠。如果你的電腦突然當機了,或者電源斷了,又或者不小心殺掉了程式,那麼最新的資料就會丟失。而AOF檔案則提供了一種更為可靠的持久化方式。每當Redis接受到會修改資料集的命令時,就會把命令追加到AOF檔案裡,當你重啟Redis時,AOF裡的命令會被重新執行一次,重建資料。

使用AOF持久化需要設定同步選項,從而確保命令同步到磁碟檔案上的時機。這是因為檔案進行寫入b並不會馬上將內容同步到磁碟上,而是先儲存到緩衝區,然後由作業系統決定什麼時候同步到磁碟。有以下同步選項:

選項 同步頻率
always 每個寫命令都同步
everysec 每秒同步一次
no 讓作業系統來決定何時同步

優點

  • 比RDB可靠。你可以制定不同的fsync策略:不進行fsync、每秒fsync一次和每次查詢進行fsync。預設是每秒fsync一次。這意味著你最多丟失一秒鐘的資料。
  • AOF日誌檔案是一個純追加的檔案。就算是遇到突然停電的情況,也不會出現日誌的定位或者損壞問題。甚至如果因為某些原因(例如磁碟滿了)命令只寫了一半到日誌檔案裡,我們也可以用redis-check-aof這個工具很簡單的進行修復。
  • 當AOF檔案太大時,Redis會自動在後臺進行重寫。重寫很安全,因為重寫是在一個新的檔案上進行,同時Redis會繼續往舊的檔案追加資料。新檔案上會寫入能重建當前資料集的最小操作命令的集合。當新檔案重寫完,Redis會把新舊檔案進行切換,然後開始把資料寫到新檔案上。
  • AOF把操作命令以簡單易懂的格式一條接一條的儲存在檔案裡,很容易匯出來用於恢復資料。例如我們不小心用FLUSHALL命令把所有資料刷掉了,只要檔案沒有被重寫,我們可以把服務停掉,把最後那條命令刪掉,然後重啟服務,這樣就能把被刷掉的資料恢復回來。

缺點

  • 在相同的資料集下,AOF檔案的大小一般會比RDB檔案大。
  • 在某些fsync策略下,AOF的速度會比RDB慢。通常fsync設定為每秒一次就能獲得比較高的效能,而在禁止fsync的情況下速度可以達到RDB的水平。
  • 在過去曾經發現一些很罕見的BUG導致使用AOF重建的資料跟原資料不一致的問題。

啟用AOF

把配置項appendonly設為yes

appendonly yes

Redis持久化背後的故事

檔案路徑和名稱

# 檔案存放目錄,與RDB共用。預設為當前工作目錄。
dir ./

# 預設檔名為appendonly.aof
appendfilename "appendonly.aof"

可靠性

你可以配置Redis呼叫fsync的頻率,有三個選項:

  • 每當有新命令追加到AOF的時候呼叫fsync。速度最慢,但是最安全。
  • 每秒fsync一次。速度快(2.4版本跟快照方式速度差不多),安全性不錯(最多丟失1秒的資料)。
  • 從不fsync,交由系統去處理。這個方式速度最快,但是安全性一般。

推薦使用每秒fsync一次的方式(預設的方式),因為它速度快,安全性也不錯。相關配置如下:

# appendfsync always
appendfsync everysec
# appendfsync no

Redis持久化背後的故事

日誌重寫

隨著寫操作的不斷增加,AOF檔案會越來越大。例如你遞增一個計數器100次,那麼最終結果就是資料集裡的計數器的值為最終的遞增結果,但是AOF檔案裡卻會把這100次操作完整的記錄下來。而事實上要恢復這個記錄,只需要1個命令就行了,也就是說AOF檔案裡那100條命令其實可以精簡為1條。所以Redis支援這樣一個功能:在不中斷服務的情況下在後臺重建AOF檔案。

工作原理如下:

  • Redis呼叫fork(),產生一個子程式。
  • 子程式把新的AOF寫到一個臨時檔案裡。
  • 主程式持續把新的變動寫到記憶體裡的buffer,同時也會把這些新的變動寫到舊的AOF裡,這樣即使重寫失敗也能保證資料的安全。
  • 當子程式完成檔案的重寫後,主程式會獲得一個訊號,然後把記憶體裡的buffer追加到子程式生成的那個新AOF裡。
  • Redis

我們可以通過配置設定日誌重寫的條件:

# Redis會記住自從上一次重寫後AOF檔案的大小(如果自Redis啟動後還沒重寫過,則記住啟動時使用的AOF檔案的大小)。
# 如果當前的檔案大小比起記住的那個大小超過指定的百分比,則會觸發重寫。
# 同時需要設定一個檔案大小最小值,只有大於這個值檔案才會重寫,以防檔案很小,但是已經達到百分比的情況。

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

Redis持久化背後的故事

要禁用自動的日誌重寫功能,我們可以把百分比設定為0:

auto-aof-rewrite-percentage 0

Redis 2.4以上才可以自動進行日誌重寫,之前的版本需要手動執行BGREWRITEAOF這個命令。

資料損壞修復

如果因為某些原因(例如伺服器崩潰)AOF檔案損壞了,導致Redis載入不了,可以通過以下方式進行修復:

  • 備份AOF檔案。

  • 使用redis-check-aof命令修復原始的AOF檔案:

    $ redis-check-aof --fix

    Redis持久化背後的故事

  • 可以使用diff -u命令看下兩個檔案的差異。

  • 使用修復過的檔案重啟Redis服務。

從RDB切換到AOF

這裡只說Redis >= 2.2版本的方式:

  • 備份一個最新的dump.rdb的檔案,並把備份檔案放在一個安全的地方。

  • 執行以下兩條命令:

    $ redis-cli config set appendonly yes
    $ redis-cli config set save ""
  • 確保資料跟切換前一致。

  • 確保資料正確的寫到AOF檔案裡。

第二條命令是用來禁用RDB的持久化方式,但是這不是必須的,因為你可以同時啟用兩種持久化方式。

記得對配置檔案redis.conf進行編輯啟用AOF,因為命令列方式修改配置在重啟Redis後就會失效。

備份

建議的備份方法:

  • 建立一個定時任務,每小時和每天建立一個快照,儲存在不同的資料夾裡。
  • 定時任務執行時,把太舊的檔案進行刪除。例如只保留48小時的按小時建立的快照和一到兩個月的按天建立的快照。
  • 每天確保一次把快照檔案傳輸到資料中心外的地方進行儲存,至少不能儲存在Redis服務所在的伺服器。

參考

相關文章