前言
什么是持久化?
持久化(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文件了
