前言
什麼是持久化?
持久化(Persistence),即把資料(如記憶體中的對象)儲存到可永久儲存的儲存設備中(如磁盤)。持久化的主要應用是将記憶體中的對象存儲在資料庫中,或者存儲在磁盤檔案中、XML資料檔案中等等。
持久化是将程式資料在持久狀态和瞬時狀态間轉換的機制。 ----摘自百度百科
Redis的資料都是存儲在記憶體中的,是以Redis持久化也就是要把Redis存儲在記憶體中的資料儲存到硬碟。
Redis提供了兩種持久化方式
- RDB持久化(快照)
- AOF持久化(隻追加操作的檔案 Append-only file)
先來看看RDB持久化
RDB持久化
RDB持久化是指在用戶端輸入
save
、
bgsave
或者達到配置檔案自動儲存快照條件時,将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指令的工作原理如下
- 伺服器程序pid為1349派生出一個pid為1357的子程序,
- 子程序将資料寫入到一個臨時 RDB 檔案中
- 當子程序完成對新 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檔案并還原資料庫的步驟如下
- 建立一個不帶網絡連接配接的僞用戶端
- 從AOF檔案中分析并讀取出一條寫指令
- 使用僞用戶端執行被讀出的寫指令
- 一直執行步驟2、3,知道AOF檔案中的所有寫指令都被處理完畢為止
這時可能會出現一個問題。伺服器可能在程式正在對 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
重寫步驟
- 建立子程序進行AOF重寫
- 将用戶端的寫指令追加到AOF重寫緩沖區
- 子程序完成AOF重寫工作後,會向父程序發送一個信号
- 父程序接收到信号後,将AOF重寫緩沖區的所有内容寫入到新AOF檔案中
- 對新的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檔案了