redis系列:RDB持久化與AOF持久化

雲梟發表於2019-03-02

前言

什麼是持久化?

持久化(Persistence),即把資料(如記憶體中的物件)儲存到可永久儲存的儲存裝置中(如磁碟)。持久化的主要應用是將記憶體中的物件儲存在資料庫中,或者儲存在磁碟檔案中、XML資料檔案中等等。 持久化是將程式資料在持久狀態和瞬時狀態間轉換的機制。 ----摘自百度百科

Redis的資料都是儲存在記憶體中的,所以Redis持久化也就是要把Redis儲存在記憶體中的資料儲存到硬碟。
Redis提供了兩種持久化方式

  1. RDB持久化(快照)
  2. AOF持久化(只追加操作的檔案 Append-only file)

先來看看RDB持久化

RDB持久化

RDB持久化是指在客戶端輸入savebgsave或者達到配置檔案自動儲存快照條件時,將Redis 在記憶體中的資料生成快照儲存在名字為 dump.rdb(檔名可修改)的二進位制檔案中。

save命令

save命令會阻塞Redis伺服器程式,直到RDB檔案建立完畢為止,在Redis伺服器阻塞期間,伺服器不能處理任何命令請求。 在客戶端輸入save

192.168.17.101:6379> save
OK
複製程式碼

服務端會出現下方字元

1349:M 30 Jul 17:16:48.935 * DB saved on disk
複製程式碼

bgsave命令

bgsave命令的工作原理如下

  1. 伺服器程式pid為1349派生出一個pid為1357的子程式,
  2. 子程式將資料寫入到一個臨時 RDB 檔案中
  3. 當子程式完成對新 RDB 檔案的寫入時,Redis 用新 RDB 檔案替換原來的 RDB 檔案,並刪除舊的 RDB 檔案。

在客戶端輸入bgsave

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

服務端會出現下方字元

1349:M 30 Jul 17:14:42.991 * Background saving started by pid 1357
1357:C 30 Jul 17:14:42.993 * DB saved on disk
1357:C 30 Jul 17:14:42.993 * RDB: 4 MB of memory used by copy-on-write
1349:M 30 Jul 17:14:43.066 * Background saving terminated with success
複製程式碼

:bgsave命令執行期間 SAVE命令會被拒絕 不能同時執行兩個BGSAVE命令 不能同時執行BGREWRITEAOF和BGSAVE命令

自動儲存

這個需要在配置檔案redis.conf中修改,預設的儲存策略如下

save 900 1    # 900 秒內有至少有 1 個鍵被改動
save 300 10   # 300 秒內有至少有 10 個鍵被改動
save 60 10000 # 60 秒內有至少有 1000 個鍵被改動
複製程式碼

接下來看看RBD的配置有哪些

配置

################################ SNAPSHOTTING  ################################
# 觸發自動儲存快照
# save <seconds> <changes>
# save <秒> <修改的次數>
save 900 1    
save 300 10   
save 60 10000 

# 設定在儲存快照出錯時,是否停止redis命令的寫入
stop-writes-on-bgsave-error yes

# 是否在匯出.rdb資料庫檔案的時候採用LZF壓縮
rdbcompression yes

#  是否開啟CRC64校驗
rdbchecksum yes

# 匯出資料庫的檔名稱
dbfilename dump.rdb

# 匯出的資料庫所在的目錄
dir ./

複製程式碼

優點

  • RDB是一個非常緊湊(有壓縮)的檔案,它儲存了某個時間點的資料,非常適用於資料的備份。
  • RDB作為一個非常緊湊(有壓縮)的檔案,可以很方便傳送到另一個遠端資料中心 ,非常適用於災難恢復.
  • RDB在儲存RDB檔案時父程式唯一需要做的就是fork出一個子程式,接下來的工作全部由子程式來做,父程式不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的效能.
  • 與AOF相比,在恢復大的資料集的時候,RDB方式會更快一些.

翻譯來自http://www.redis.cn

缺點

  • Redis意外當機 時,會丟失部分資料
  • 當Redis資料量比較大時,fork的過程是非常耗時的,fork子程式時是會阻塞的,在這期間Redis 是不能響應客戶端的請求的。

AOF持久化

AOF持久化是通過儲存Redis伺服器所執行的寫命令來記錄資料庫狀態,也就是每當 Redis 執行一個改變資料集的命令時(比如 SET), 這個命令就會被追加到 AOF 檔案的末尾。

那麼我們如何開啟AOF持久化功能呢?

開啟AOF持久化

修改redis.conf配置檔案,預設是appendonly no(關閉狀態),將no改為yes即可

appendonly yes
複製程式碼

在客戶端輸入如下命令也可,但是Redis伺服器重啟後會失效

192.168.17.101:6379> config set appendonly yes
OK
複製程式碼

接下來看看AOF持久化功能的實現

實現

AOF持久化功能的實現可以分為命令追加(append)、檔案寫入和檔案同步(sync)三個步驟。下面就是三個步驟的整個過程。

在Redis客戶端輸入如下命令

192.168.17.101:6379> set learnRedis testAOF
OK
複製程式碼

appendonly.aof檔案會增加如下內容

*2
$6
SELECT
$1
0
*3
$3
set
$10
learnRedis
$7
testAOF

複製程式碼

命令追加

AOF持久化功能開啟時,伺服器在執行完一個寫命令之後,會以協議格式將被執行的寫命令追加到伺服器狀態的aof_buf緩衝區的末尾。此時緩衝區的記錄還沒有寫入到appendonly.aof檔案中。

檔案的寫入和同步

為什麼將檔案寫入和檔案同步合在一塊講呢?因為配置檔案中提供了一個appendfsync引數,這個引數控制著檔案寫入和同步的行為。

關於檔案的寫入和同步的資料如下

因為為了提高檔案的寫入效率,在現代作業系統中,當使用者呼叫write函式,將一些資料寫入到檔案的時候,os通常會將寫入資料暫時儲存在一個記憶體緩衝區裡面(例如,unix系統實現在核心中設有緩衝區快取記憶體或頁快取記憶體,當我們向檔案寫入資料時,核心通常先將資料複製到緩衝區中,然後排入佇列,晚些時候再寫入磁碟),這種方式稱為延遲寫,等到緩衝區的空間被填滿,或者超過了指定的時限,或者核心需要重用緩衝區存放其它磁碟塊資料時,才會真正將緩衝區中的所有資料寫入到磁碟裡面。

簡單來說就是

檔案寫入:只是寫入到了記憶體緩衝區,可能還沒有寫到檔案所擁有的磁碟資料塊上
檔案同步:將緩衝區中的內容沖洗到磁碟上

appendfsync引數

appendfsync選項的值 效果
always 每次有新命令時,就將緩衝區資料寫入並同步到 AOF 檔案
everysec(預設) 每秒將緩衝區的資料寫入並同步到 AOF 檔案
no 將緩衝區資料寫入AOF 檔案,但是同步操作到交給作業系統來處理

載入與資料還原

讀取AOF檔案並還原資料庫的步驟如下

  1. 建立一個不帶網路連線的偽客戶端
  2. 從AOF檔案中分析並讀取出一條寫命令
  3. 使用偽客戶端執行被讀出的寫命令
  4. 一直執行步驟2、3,知道AOF檔案中的所有寫命令都被處理完畢為止

圖片來自Redis設計與實現

這時可能會出現一個問題。伺服器可能在程式正在對 AOF 檔案進行寫入時停機,造成了 AOF 檔案出錯,那麼 Redis 在重啟時會拒絕載入這個 AOF 檔案,從而確保資料的一致性不會被破壞 當發生這種情況時, 可以用以下方法來修復出錯的 AOF 檔案:

  • 為現有的 AOF 檔案建立一個備份。
  • 使用 Redis 附帶的 redis-check-aof 程式,對原來的 AOF 檔案進行修復: redis-check-aof –fix
  • (可選)使用 diff -u 對比修復後的 AOF 檔案和原始 AOF 檔案的備份,檢視兩個檔案之間的不同之處。
  • 重啟 Redis 伺服器,等待伺服器載入修復後的 AOF 檔案,並進行資料恢復。

另外redis.conf配置檔案中還提供了一個引數來控制是否忽略最後一條可能存在問題的指令,如下

aof-load-truncated yes
複製程式碼

重寫

由於AOF 持久化是通過不斷地將命令追加到檔案的末尾來記錄資料庫狀態的, 所以隨著寫入命令的不斷增加, AOF 檔案的體積也會變得越來越大。 且有些命令是改變同一資料,是可以合併成一條命令的。就好比對一個計數器呼叫了 100 次 INCR,AOF就會存入100 條記錄,其實存入一條資料就可以了。

所以為了處理這種情況,Redis提供了AOF重寫機制。

AOF重寫機制的觸發有兩種機制,一個是通過呼叫命令BGREWRITEAOF

192.168.17.101:6379> BGREWRITEAOF
Background append only file rewriting started
複製程式碼

另一種是根據配置檔案中的引數觸發,引數如下

auto-aof-rewrite-percentage 100 #當前AOF檔案大小和上一次重寫時AOF檔案大小的比值
auto-aof-rewrite-min-size 64mb  #檔案的最小體積
複製程式碼

服務端會出現如下資訊

1349:M 30 Jul 17:19:25.311 * Background append only file rewriting started by pid 1392
1349:M 30 Jul 17:19:25.379 * AOF rewrite child asks to stop sending diffs.
1392:C 30 Jul 17:19:25.379 * Parent agreed to stop sending diffs. Finalizing AOF...
1392:C 30 Jul 17:19:25.380 * Concatenating 0.00 MB of AOF diff received from parent.
1392:C 30 Jul 17:19:25.380 * SYNC append only file rewrite performed
1392:C 30 Jul 17:19:25.381 * AOF rewrite: 4 MB of memory used by copy-on-write
1349:M 30 Jul 17:19:25.466 * Background AOF rewrite terminated with success
1349:M 30 Jul 17:19:25.467 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
1349:M 30 Jul 17:19:25.467 * Background AOF rewrite finished successfully
複製程式碼

重寫步驟

  1. 建立子程式進行AOF重寫
  2. 將客戶端的寫命令追加到AOF重寫緩衝區
  3. 子程式完成AOF重寫工作後,會向父程式傳送一個訊號
  4. 父程式接收到訊號後,將AOF重寫緩衝區的所有內容寫入到新AOF檔案中
  5. 對新的AOF檔案進行改名,原子的覆蓋現有的AOF檔案

:AOF重寫不需要對現有的AOF檔案進行任何讀取、分析和寫入操作。

配置

############################## APPEND ONLY MODE ###############################

# 是否開啟AOF功能
appendonly no

# AOF檔案件名稱
appendfilename "appendonly.aof"

# 寫入AOF檔案的三種方式
# appendfsync always
appendfsync everysec
# appendfsync no

# 重寫AOF時,是否繼續寫AOF檔案
no-appendfsync-on-rewrite no

# 自動重寫AOF檔案的條件
auto-aof-rewrite-percentage 100 #百分比
auto-aof-rewrite-min-size 64mb #大小

# 是否忽略最後一條可能存在問題的指令
aof-load-truncated yes

複製程式碼

優點

  • 使用AOF 會讓你的Redis更加持久化
  • AOF檔案是一個只進行追加的日誌檔案,不需要在寫入時讀取檔案。
  • Redis 可以在 AOF 檔案體積變得過大時,自動地在後臺對 AOF 進行重寫 。
  • AOF檔案可讀性高,分析容易

缺點

  • 對於相同的資料來說,AOF 檔案大小通常要大於 RDB 檔案
  • 根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB

資料載入

RDB和AOF都是在啟動時載入的,AOF開啟時,會優先從AOF檔案從恢復資料 ,AOF關閉時才會從RDB檔案恢復資料。

注:不知從什麼版本開始,開啟AOF功能時AOF檔案不存在也不會載入RDB檔案了

相關文章