1. 程式人生 > 程式設計 >Redis持久化機制:RDB和AOF

Redis持久化機制:RDB和AOF

Redis資料持久化

Redis作為一個記憶體資料庫,資料是以記憶體為載體儲存的,那麼一旦Redis伺服器程式退出,伺服器中的資料也會消失。為瞭解決這個問題,Redis提供了持久化機制,也就是把記憶體中的資料儲存到磁碟當中,避免資料意外丟失

Redis提供了兩種持久化方案:RDB持久化AOF持久化,一個是快照的方式,一個是類似日誌追加的方式

RDB快照持久化

RDB持久化是通過快照的方式,即在指定的時間間隔內將記憶體中的資料集快照寫入磁碟。在建立快照之後,使用者可以備份該快照,可以將快照複製到其他伺服器以建立相同資料的伺服器副本,或者在重啟伺服器後恢復資料。RDB是Redis預設的持久化方式

快照持久化

RDB持久化會生成RDB檔案,該檔案是一個壓縮過的二進位制檔案,可以通過該檔案還原快照時的資料庫狀態,即生成該RDB檔案時的伺服器資料。RDB檔案預設為當前工作目錄下的dump.rdb,可以根據配置檔案中的dbfilenamedir設定RDB的檔名和檔案位置

# 設定 dump 的檔名
dbfilename dump.rdb

# 工作目錄
# 例如上面的 dbfilename 只指定了檔名,
# 但是它會寫入到這個目錄下。這個配置項一定是個目錄,而不能是檔名。
dir ./
複製程式碼

觸發快照的時機

  • 執行savebgsave命令
  • 配置檔案設定save <seconds> <changes>
    規則,自動間隔性執行bgsave命令
  • 主從複製時,從庫全量複製同步主庫資料,主庫會執行bgsave
  • 執行flushall命令清空伺服器資料
  • 執行shutdown命令關閉Redis時,會執行save命令

save和bgsave命令

執行savebgsave命令,可以手動觸發快照,生成RDB檔案,兩者的區別如下

使用save命令會阻塞Redis伺服器程式,伺服器程式在RDB檔案建立完成之前是不能處理任何的命令請求

127.0.0.1:6379> save
OK
複製程式碼

而使用bgsave命令不同的是,basave命令會fork一個子程式,然後該子程式會負責建立RDB檔案,而伺服器程式會繼續處理命令請求

127.0.0.1:6379> bgsave
Background saving started
複製程式碼

fork()是由作業系統提供的函式,作用是建立當前程式的一個副本作為子程式

fork一個子程式,子程式會把資料集先寫入臨時檔案,寫入成功之後,再替換之前的RDB檔案,用二進位制壓縮儲存,這樣可以保證RDB檔案始終儲存的是完整的持久化內容

自動間隔觸發

在配置檔案中設定save <seconds> <changes>規則,可以自動間隔性執行bgsave命令

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""

save 900 1
save 300 10
save 60 10000
複製程式碼

save <seconds> <changes>表示在seconds秒內,至少有changes次變化,就會自動觸發gbsave命令

  • save 900 1 當時間到900秒時,如果至少有1個key發生變化,就會自動觸發bgsave命令建立快照
  • save 300 10 當時間到300秒時,如果至少有10個key發生變化,就會自動觸發bgsave命令建立快照
  • save 60 10000 當時間到60秒時,如果至少有10000個key發生變化,就會自動觸發bgsave命令建立快照

AOF持久化

除了RDB持久化,Redis還提供了AOF(Append Only File)持久化功能,AOF持久化會把被執行的寫命令寫到AOF檔案的末尾,記錄資料的變化。預設情況下,Redis是沒有開啟AOF持久化的,開啟後,每執行一條更改Redis資料的命令,都會把該命令追加到AOF檔案中,這是會降低Redis的效能,但大部分情況下這個影響是能夠接受的,另外使用較快的硬碟可以提高AOF的效能

可以通過配置redis.conf檔案開啟AOF持久化,關於AOF的配置如下

# appendonly引數開啟AOF持久化
appendonly no

# AOF持久化的檔名,預設是appendonly.aof
appendfilename "appendonly.aof"

# AOF檔案的儲存位置和RDB檔案的位置相同,都是通過dir引數設定的
dir ./

# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no

# aof重寫期間是否同步
no-appendfsync-on-rewrite no

# 重寫觸發配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 載入aof出錯如何處理
aof-load-truncated yes

# 檔案重寫策略
aof-rewrite-incremental-fsync yes
複製程式碼

AOF的實現

AOF需要記錄Redis的每個寫命令,步驟為:命令追加(append)、檔案寫入(write)和檔案同步(sync)

命令追加(append)

開啟AOF持久化功能後,伺服器每執行一個寫命令,都會把該命令以協議格式先追加到aof_buf快取區的末尾,而不是直接寫入檔案,避免每次有命令都直接寫入硬碟,減少硬碟IO次數

檔案寫入(write)和檔案同步(sync)

對於何時把aof_buf緩衝區的內容寫入儲存在AOF檔案中,Redis提供了多種策略

  • appendfsync always:將aof_buf緩衝區的所有內容寫入並同步到AOF檔案,每個寫命令同步寫入磁碟
  • appendfsync everysec:將aof_buf快取區的內容寫入AOF檔案,每秒同步一次,該操作由一個執行緒專門負責
  • appendfsync no:將aof_buf快取區的內容寫入AOF檔案,什麼時候同步由作業系統來決定

appendfsync選項的預設配置為everysec,即每秒執行一次同步

關於AOF的同步策略是涉及到作業系統的write函式和fsync函式的,在《Redis設計與實現》中是這樣說明的

為了提高檔案寫入效率,在現代作業系統中,當使用者呼叫write函式,將一些資料寫入檔案時,作業系統通常會將資料暫存到一個記憶體緩衝區裡,當緩衝區的空間被填滿或超過了指定時限後,才真正將緩衝區的資料寫入到磁碟裡。

這樣的操作雖然提高了效率,但也為資料寫入帶來了安全問題:如果計算機停機,記憶體緩衝區中的資料會丟失。為此,系統提供了fsyncfdatasync同步函式,可以強製作業系統立刻將緩衝區中的資料寫入到硬碟裡,從而確保寫入資料的安全性。

從上面的介紹我們知道,我們寫入的資料,作業系統並不一定會馬上同步到磁碟,所以Redis才提供了appendfsync的選項配置。當該選項時為always時,資料安全性是最高的,但是會對磁碟進行大量的寫入,Redis處理命令的速度會受到磁碟效能的限制;appendfsync everysec選項則兼顧了資料安全和寫入效能,以每秒一次的頻率同步AOF檔案,即便出現系統崩潰,最多隻會丟失一秒內產生的資料;如果是appendfsync no選項,Redis不會對AOF檔案執行同步操作,而是有作業系統決定何時同步,不會對Redis的效能帶來影響,但假如系統崩潰,可能會丟失不定數量的資料

AOF重寫(rewrite)

在瞭解AOF重寫之前,我們先來看看AOF檔案中儲存的內容是啥,先執行兩個寫操作

127.0.0.1:6379> set s1 hello
OK
127.0.0.1:6379> set s2 world
OK
複製程式碼

然後我們開啟appendonly.aof檔案,可以看到如下內容

*3
$3
set
$2
s1
$5
hello
*3
$3
set
$2
s2
$5
world
複製程式碼

該命令格式為Redis的序列化協議(RESP)。*3代表這個命令有三個引數,$3表示該引數長度為3

看了上面的AOP檔案的內容,我們應該能想象,隨著時間的推移,Redis執行的寫命令會越來越多,AOF檔案也會越來越大,過大的AOF檔案可能會對Redis伺服器造成影響,如果使用AOF檔案來進行資料還原所需時間也會越長

時間長了,AOF檔案中通常會有一些冗餘命令,比如:過期資料的命令、無效的命令(重複設定、刪除)、多個命令可合併為一個命令(批處理命令)。所以AOF檔案是有精簡壓縮的空間的

AOF重寫的目的就是減小AOF檔案的體積,不過值得注意的是:AOF檔案重寫並不需要對現有的AOF檔案進行任何讀取、分享和寫入操作,而是通過讀取伺服器當前的資料庫狀態來實現的

檔案重寫可分為手動觸發和自動觸發,手動觸發執行bgrewriteaof命令,該命令的執行跟bgsave觸發快照時類似的,都是先fork一個子程式做具體的工作

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
複製程式碼

自動觸發會根據auto-aof-rewrite-percentageauto-aof-rewrite-min-size 64mb配置來自動執行bgrewriteaof命令

# 表示當AOF檔案的體積大於64MB,且AOF檔案的體積比上一次重寫後的體積大了一倍(100%)時,會執行`bgrewriteaof`命令
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
複製程式碼

下面看一下執行bgrewriteaof命令,重寫的流程

  • 重寫會有大量的寫入操作,所以伺服器程式會fork一個子程式來建立一個新的AOF檔案
  • 在重寫期間,伺服器程式繼續處理命令請求,如果有寫入的命令,追加到aof_buf的同時,還會追加到aof_rewrite_bufAOF重寫緩衝區
  • 當子程式完成重寫之後,會給父程式一個訊號,然後父程式會把AOF重寫緩衝區的內容寫進新的AOF臨時檔案中,再對新的AOF檔案改名完成替換,這樣可以保證新的AOF檔案與當前資料庫資料的一致性

資料恢復

Redis4.0開始支援RDB和AOF的混合持久化(可以通過配置項 aof-use-rdb-preamble 開啟)

  • 如果是redis程式掛掉,那麼重啟redis程式即可,直接基於AOF日誌檔案恢復資料
  • 如果是redis程式所在機器掛掉,那麼重啟機器後,嘗試重啟redis程式,嘗試直接基於AOF日誌檔案進行資料恢復,如果AOF檔案破損,那麼用redis-check-aof fix命令修復
  • 如果沒有AOF檔案,會去載入RDB檔案
  • 如果redis當前最新的AOF和RDB檔案出現了丟失/損壞,那麼可以嘗試基於該機器上當前的某個最新的RDB資料副本進行資料恢復

RDB vs AOF

上面介紹了RDB持久化和AOF持久化,那麼來看一下他們各自的優缺點以及該如何選擇持久化方案

RDB和AOF優缺點

關於RDB和AOF的優缺點,官網上面也給了比較詳細的說明redis.io/topics/pers…

RDB

優點:

  • RDB快照是一個壓縮過的非常緊湊的檔案,儲存著某個時間點的資料集,適合做資料的備份,災難恢復
  • 可以最大化Redis的效能,在儲存RDB檔案,伺服器程式只需fork一個子程式來完成RDB檔案的建立,父程式不需要做IO操作
  • 與AOF相比,恢復大資料集的時候會更快

缺點:

  • RDB的資料安全性是不如AOF的,儲存整個資料集的過程是比繁重的,根據配置可能要幾分鐘才快照一次,如果伺服器宕機,那麼就可能丟失幾分鐘的資料
  • Redis資料集較大時,fork的子程式要完成快照會比較耗CPU、耗時

AOF

優點:

  • 資料更完整,安全性更高,秒級資料丟失(取決fsync策略,如果是everysec,最多丟失1秒的資料)
  • AOF檔案是一個只進行追加的日誌檔案,且寫入操作是以Redis協議的格式儲存的,內容是可讀的,適合誤刪緊急恢復

缺點:

  • 對於相同的資料集,AOF檔案的體積要大於RDB檔案,資料恢復也會比較慢
  • 根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB。 不過在一般情況下, 每秒 fsync 的效能依然非常高

如何選擇RDB和AOF

  • 如果是資料不那麼敏感,且可以從其他地方重新生成補回的,那麼可以關閉持久化
  • 如果是資料比較重要,不想再從其他地方獲取,且可以承受數分鐘的資料丟失,比如快取等,那麼可以只使用RDB
  • 如果是用做記憶體資料庫,要使用Redis的持久化,建議是RDB和AOF都開啟,或者定期執行bgsave做快照備份,RDB方式更適合做資料的備份,AOF可以保證資料的不丟失