redis持久化有兩種方式:RDB和AOF。分別對應着全量複製和增量複製。深入理解各自的實現方式及適用場景對redis的使用和運維十分重要。下面就分別介紹。html
RDB持久化即將當前Redis實例中的數據快照所有保存的磁盤的過程。可手動觸發,也可根據配置自動觸發。redis
手動觸發有兩個命令能夠選擇: save
和bgsave
。二者區別在於save
是阻塞的,複製完成前會阻塞客戶端命令執行,目前已經廢棄。bgsave
是非阻塞的。執行過程當中redis進程會fork出一個子進程負責複製任務,而主進程依然響應客戶端請求命令。對應bgsave
命令,阻塞只存在於fork
過程當中。大大減少了阻塞的時間。緩存
如圖,上次rdb同步時間在17:20安全
執行命令bgsave
後, 返回 Background saving started
,bash
查看rdb文件更新時間,已變爲當前時間。服務器
若使用自動觸發,需配置save參數。格式 save m n
。查看redis配置文件可看到相關配置。app
save 900 1
save 300 10
save 60 10000
複製代碼
配置 save m n
含義爲當數據在m秒內發生n次修改,自動執行bgsave
命令進行RDB持久化。如save 900 1
表示在900秒(15分鐘內)數據有至少1次修改,則觸發RDB。多個配置狀況下,只要符合任一條件便可。運維
具體在實現上,redis服務器會記錄自上次RDB備份後,有多少次修改。每100ms檢查一次,看是否有符合條件的save配置,有則再次執行DRB.性能
另外,當在客戶端執行關閉服務器命令shutdown
,若redis沒有開啓AOF,則自動執行bgsave
進行備份。學習
因爲save的持久化命令會發送阻塞,因此目前基本是用bgsave命令觸發的RDB,關於bgsave的流程以下圖所示。
簡要流程以下
bgsave
命令後,調用fork命令建立子進程。期間主進程是阻塞的(圖中1,2)。Background saving started
,再也不阻塞,照常響應客戶端命令。(圖中3)簡要來講是父進程fork出了一個子進程用於持久化任務,父進程仍響應客戶端請求,耗時的RDB讓子進程來作。這樣的確能夠大大減小阻塞時間,可有一個問題是,主進程還在接受請求,子進程怎樣進行復制?難道子進程再複製一份主進程的內存用於複製,這樣內存豈不是要翻倍?確定不能這樣。要是子進程共享父進程的內存,那怎樣保證邊接受請求邊複製呢?
事實上,fork操做使用寫時複製(copy-on-write)技術實現。建立的子進程共享父進程地址空間,當只有須要寫入時纔會複製地址空間。也就是說,redis的子進程使用共享的內存快照完成RDB備份,而主進程當收到寫請求後,會將涉及到的內存頁複製出一份副本,在此副本進行修改。當子進程RDB完成後,通知主進程更換副本,RDB就此完成。
dir
設置,文件名由dbfilename
指定。RDB文件爲二進制文件,可開啓壓縮處理,壓縮後文件體積可大大縮小。使用rdbcompression
參數指定,默認爲yes
。如下是redis配置文件有關rdb的默認配置
## 是否開啓rdb文件壓縮
rdbcompression yes
## rdb文件效驗
rdbchecksum yes
## rdb文件名
dbfilename redis-dump-127.0.0.1-6379.rdb
## 備份文件儲存目錄
dir /usr/local/var/db/redis/
複製代碼
AOF主要用於解決redis實時持久化的問題。方式爲實時將redis全部寫命令記錄下來,用於之後恢復及備份。
aof默認是關閉的,使用appendonly yes
開啓。用appendfilename
設置aof文件名。同rdb同樣,dir
表示aof儲存目錄。
在配置文件設好後重啓redis,即開啓了aof功能。可以使用info persistence
可查看redis持久化的參數,以下
aof_enabled:1
表示已開啓aof功能。
AOF寫入日誌的直接就是文本格式。如set hello world
這個命令,在appendonly.aof
文件中儲存的就是
*3
$3
SET
$5
hello
$5
world
複製代碼
這就是Redis客戶端與服務器通訊的的序列化協議(RESP),每行用\r\n
分割,*n表示參數個數,$n表示字節數,十分簡單明瞭。AOF直接使用協議格式可以讓redis直接識別。無需特殊處理,避免二次處理的開銷,也方便修改。
AOF的流程以下
用流程圖表示就是
爲何不直接將命令寫入磁盤很容易理解,這裏需關注的是以什麼策略從緩衝區寫到磁盤。redis提供了3種策略。
策略 | 說明 |
---|---|
always | 每次命令寫入緩衝區後都同步刷新到硬盤(使用fsync命令刷新磁盤),此方式最安全,但也是最慢的,由於每次寫數據都要同步硬盤 |
everysec | 每次寫命令調用系統命令write操做,write只會將數據寫入系統緩衝區,而後由專門的線程每秒同步刷盤 |
no | 每次寫命令調用系統命令write操做寫入系統緩衝區,具體刷盤時間由操做系統決定,性能是最高的,但數據安全性不能保證 |
一般,咱們選擇everysec
做爲刷盤策略,也是redis默認配置,能夠兼顧性能和數據安全。
通常咱們使用everysec
的同步刷盤策略。此時會有一個專門的線程每秒執行一次刷盤操做。爲保證數據安全性,redis主進程會比對上次刷盤時間與當前時間的差值,若是大於2秒,則阻塞以等待刷盤完成。
也就是說,若是硬盤性能不好,fsync
執行太慢,會形成redis阻塞。以保證硬盤的數據不被真實內存數據落的太遠(最大2秒的數據差)。
剛纔提到,AOF直接使用redis的序列化協議進行備份,一段時間後,aof文件會很大。爲解決此問題,可配置aof重寫機制對原aof文件進行瘦身。
AOF重寫的實質即把redis進程的內存數據轉爲寫命令從新生成一份aof文件,以替代原aof文件。
重寫後比原aof體積小的緣由有如下幾點
hset k1 f1 v1
,hset k1 f2 v2
可合併hmset k1 f1 v1 f2 v2
)set hello a, set hello b
等)AOF可有手動觸發和自動觸發。
手動觸發使用bgrewriteaof
命令觸發。
自動觸發有關參數及含義以下
## aof重寫時此時aof文件與上次aof文件大小百分比
auto-aof-rewrite-percentage 100
## aof重寫時文件最小大小,默認64M
auto-aof-rewrite-min-size 64mb
複製代碼
如按默認配置,則發生重寫的條件爲aof文件大於64M且此時aof文件比上次重寫時大100%,便是上次的2倍。
aof重寫雖然比較複雜,但對比圖,也能很容易明白其中流程。這這裏有個疑問是爲何主進程在子進程重寫時爲何要維護兩個緩衝區?只維護一個不行嗎?畢竟原有aof文件等到新aof文件生成後也是要替換的。
仔細想一想就可知,有必要維護兩個緩衝區。主要是須要維護原有aof邏輯不變,以防aof重寫失敗致使數據丟失。另外一點兩個緩衝區目的也不一樣,不宜混淆。
注意:
爲防止資源過於競爭,同一時刻只能進行一個AOF重寫任務,當進行重寫時發現有RDB任務時,也會等RDB完成後進行。
在重啓時,redis會首先判斷是否開啓aof,若開啓aof且aof文件存在,則加載aof文件進行數據恢復以啓動。不然判斷rdb文件存在並加載rdb以啓動。
在進行RDB備份或是AOF重寫時,都須要fork出子進程運行任務。此期間會大量消耗CPU,一般子進程對單核CPU的利用率達到90%,所以在有RDB或需AOF重寫任務的redis,不要作綁定單核CPU操做,這會致使父子進程對CPU產生競爭。