Redis支持RDB和AOF兩種持久化機(jī)制,持久化功能有效地避免因進(jìn)程退出造成的數(shù)據(jù)丟失問(wèn)題,當(dāng)下次重啟時(shí)利用之前持久化的文件即可實(shí)現(xiàn)數(shù)據(jù)恢復(fù)。Redis支持兩種方式的持久化,一種是RDB方式,一種是AOF方式??梢詥为?dú)使用其中一種或?qū)⒍呓Y(jié)合使用。
RDB持久化
RDB持久化是把當(dāng)前進(jìn)程數(shù)據(jù)生成快照保存到硬盤的過(guò)程,觸發(fā)RDB持久化過(guò)程分為手動(dòng)觸發(fā)和自動(dòng)觸發(fā)。
手動(dòng)觸發(fā)分別對(duì)應(yīng)save和bgsave命令:
save命令:阻塞當(dāng)前Redis服務(wù)器,直到RDB過(guò)程完成為止,對(duì)于內(nèi)存比較大的實(shí)例會(huì)造成長(zhǎng)時(shí)間阻塞,線上環(huán)境不建議使用。
bgsave命令:Redis進(jìn)程執(zhí)行fork操作創(chuàng)建子進(jìn)程,RDB持久化過(guò)程由子進(jìn)程負(fù)責(zé),完成后自動(dòng)結(jié)束。阻塞只發(fā)生在fork階段,一般時(shí)間很短。
自動(dòng)觸發(fā)
除了執(zhí)行命令手動(dòng)觸發(fā)之外,Redis內(nèi)部還存在自動(dòng)觸發(fā)RDB的持久化機(jī)制。如以下場(chǎng)景:
1)使用save相關(guān)配置,如“save m n”。表示m秒內(nèi)數(shù)據(jù)集存在n次修改時(shí),自動(dòng)觸發(fā)bgsave。
2)如果從節(jié)點(diǎn)執(zhí)行全量復(fù)制操作,主節(jié)點(diǎn)自動(dòng)執(zhí)行bgsave生成RDB文件并發(fā)送給從節(jié)點(diǎn)
3)執(zhí)行debug reload命令重新加載Redis時(shí),也會(huì)自動(dòng)觸發(fā)save操作。
4)默認(rèn)情況下執(zhí)行shutdown命令時(shí),如果沒(méi)有開啟AOF持久化功能則自動(dòng)執(zhí)行bgsave。
快照持久化實(shí)現(xiàn)過(guò)程
bgsave是主流的觸發(fā)RDB持久化方式,它的運(yùn)作流程如下圖:

1)執(zhí)行bgsave命令,Redis父進(jìn)程判斷當(dāng)前是否存在正在執(zhí)行的子進(jìn)程,如RDB/AOF子進(jìn)程,如果存在bgsave命令直接返回。
2)父進(jìn)程執(zhí)行fork操作創(chuàng)建子進(jìn)程,fork操作過(guò)程中父進(jìn)程會(huì)阻塞,通過(guò)info stats命令查看latest_fork_usec選項(xiàng),可以獲取最近一個(gè)fork操作的耗時(shí),單位為微秒。
3)父進(jìn)程fork完成后,bgsave命令返回“Background saving started”信息并不再阻塞父進(jìn)程,可以繼續(xù)響應(yīng)其他命令。
4)子進(jìn)程創(chuàng)建RDB文件,根據(jù)父進(jìn)程內(nèi)存生成臨時(shí)快照文件,完成后對(duì)原有文件進(jìn)行原子替換。執(zhí)行l(wèi)astsave命令可以獲取最后一次生成RDB的時(shí)間,對(duì)應(yīng)info統(tǒng)計(jì)的rdb_last_save_time選項(xiàng)。
5)進(jìn)程發(fā)送信號(hào)給父進(jìn)程表示完成,父進(jìn)程更新統(tǒng)計(jì)信息。
RDB文件的處理
保存:RDB文件保存在dir配置指定的目錄下,文件名通過(guò)dbfilename配置指定??梢酝ㄟ^(guò)執(zhí)行config set dir{newDir}和config setdbfilename{newFileName}運(yùn)行期動(dòng)態(tài)執(zhí)行,當(dāng)下次運(yùn)行時(shí)RDB文件會(huì)保存到新目錄。
壓縮:Redis默認(rèn)采用LZF算法對(duì)生成的RDB文件做壓縮處理,壓縮后的文件遠(yuǎn)遠(yuǎn)小于內(nèi)存大小,默認(rèn)開啟,可以通過(guò)參數(shù)config set rdbcompression{yes|no}動(dòng)態(tài)修改。
校驗(yàn):如果Redis加載損壞的RDB文件時(shí)拒絕啟動(dòng),并打印如下日志:
#?Short?read?or?OOM?loading?DB.?Unrecoverable?error,?aborting?now.
這時(shí)可以使用Redis提供的redis-check-dump工具檢測(cè)RDB文件并獲取對(duì)應(yīng)的錯(cuò)誤報(bào)告。
RDB的優(yōu)缺點(diǎn)
RDB的優(yōu)點(diǎn):
RDB是一個(gè)緊湊壓縮的二進(jìn)制文件,代表Redis在某個(gè)時(shí)間點(diǎn)上的數(shù)據(jù)快照。非常適用于備份,全量復(fù)制等場(chǎng)景。
Redis加載RDB恢復(fù)數(shù)據(jù)遠(yuǎn)遠(yuǎn)快于AOF的方式。
RDB的缺點(diǎn):
RDB方式數(shù)據(jù)沒(méi)辦法做到實(shí)時(shí)持久化/秒級(jí)持久化。bgsave每次運(yùn)行都要執(zhí)行fork操作創(chuàng)建子進(jìn)程,屬于重量級(jí)操作,頻繁執(zhí)行成本過(guò)高。
RDB文件使用特定二進(jìn)制格式保存,Redis版本演進(jìn)過(guò)程中有多個(gè)格式的RDB版本,存在老版本Redis服務(wù)無(wú)法兼容新版RDB格式的問(wèn)題。
AOF(append only file)持久化
AOF(append only file)持久化:以獨(dú)立日志的方式記錄每次寫命令,重啟時(shí)再重新執(zhí)行AOF文件中的命令達(dá)到恢復(fù)數(shù)據(jù)的目的。AOF的主要作用是解決了數(shù)據(jù)持久化的實(shí)時(shí)性,目前已經(jīng)是Redis持久化的主流方式。
開啟AOF功能
開啟AOF功能需要設(shè)置配置:appendonly yes,默認(rèn)不開啟。AOF文件名通過(guò)appendfilename配置設(shè)置,默認(rèn)文件名是appendonly.aof。保存路徑同RDB持久化方式一致,通過(guò)dir配置指定。
AOF的工作流程
AOF的工作流程操作:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啟加載(load)。如下圖所示:

流程如下:
1)所有的寫入命令會(huì)追加到aof_buf(緩沖區(qū))中。
2)AOF緩沖區(qū)根據(jù)對(duì)應(yīng)的策略向硬盤做同步操作。
3)隨著AOF文件越來(lái)越大,需要定期對(duì)AOF文件進(jìn)行重寫,達(dá)到壓縮的目的。
4)當(dāng)Redis服務(wù)器重啟時(shí),可以加載AOF文件進(jìn)行數(shù)據(jù)恢復(fù)。
命令寫入
AOF命令寫入的內(nèi)容直接是文本協(xié)議格式,開啟AOF后,所有寫入命令都包含追加操作,直接采用文本協(xié)議格式,避免了二次處理開銷。
文件同步
Redis提供了多種AOF緩沖區(qū)同步文件策略,由參數(shù)appendfsync控制。
always:命令寫入aof_buf后,調(diào)用系統(tǒng)ysnyc操作同步到AOF文件,ysnyc完成后線程返回。
everysec:命令寫入aof_buf后,調(diào)用系統(tǒng)write操作,write完成后線程返回。ysnyc同步文件操作由專門線程每秒調(diào)用一次。
no:命令寫入aof_buf后,調(diào)用系統(tǒng)write操作,不對(duì)AOF文件做ysnyc同步,同步硬盤操作由操作系統(tǒng)負(fù)責(zé),通常同步周期最長(zhǎng)30秒。
系統(tǒng)調(diào)用write和fsync說(shuō)明:
write操作會(huì)觸發(fā)延遲寫(delayed write)機(jī)制。Linux在內(nèi)核提供頁(yè)緩沖區(qū)用來(lái)提高硬盤IO性能。write操作在寫入系統(tǒng)緩沖區(qū)后直接返回。同步硬盤操作依賴于系統(tǒng)調(diào)度機(jī)制,如緩沖區(qū)頁(yè)空間寫滿或達(dá)到特定時(shí)間周期。同步文件之前,如果此時(shí)系統(tǒng)故障宕機(jī),緩沖區(qū)內(nèi)數(shù)據(jù)將丟失。
fsync針對(duì)單個(gè)文件操作(比如AOF文件),做強(qiáng)制硬盤同步,fsync將阻塞直到寫入硬盤完成后返回,保證了數(shù)據(jù)持久化。
重寫機(jī)制
Redis引入AOF重寫機(jī)制壓縮文件體積。AOF文件重寫是把Redis進(jìn)程內(nèi)的數(shù)據(jù)轉(zhuǎn)化為寫命令同步到新AOF文件的過(guò)程。
AOF重寫機(jī)制壓縮文件體積的原因:
1)進(jìn)程內(nèi)已經(jīng)超時(shí)的數(shù)據(jù)不再寫入文件。
2)舊的AOF文件含有無(wú)效命令,重寫使用進(jìn)程內(nèi)數(shù)據(jù)直接生成,這樣新的AOF文件只保留最終數(shù)據(jù)的寫入命令。
3)多條寫命令可以合并為一個(gè),為了防止單條命令過(guò)大造成客戶端緩沖區(qū)溢出,對(duì)于list、set、hash、zset等類型操作,以64個(gè)元素為界拆分為多條。
AOF重寫過(guò)程可以手動(dòng)觸發(fā)和自動(dòng)觸發(fā):
手動(dòng)觸發(fā):直接調(diào)用bgrewriteaof命令。
自動(dòng)觸發(fā):根據(jù)auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數(shù)確定自動(dòng)觸發(fā)時(shí)機(jī)。
auto-aof-rewrite-min-size:表示運(yùn)行AOF重寫時(shí)文件最小體積,默認(rèn)為64MB。auto-aof-rewrite-percentage:代表當(dāng)前AOF文件空間(aof_current_size)和上一次重寫后AOF文件空間(aof_base_size)的比值。
自動(dòng)觸發(fā)時(shí)機(jī)=aof_current_size>auto-aof-rewrite-min-size&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage。其中aof_current_size和aof_base_size可以在info Persistence統(tǒng)計(jì)信息中查看。
AOF重寫流程:如下圖

流程說(shuō)明:
1)執(zhí)行AOF重寫請(qǐng)求。如果當(dāng)前進(jìn)程正在執(zhí)行AOF重寫,請(qǐng)求不執(zhí)行并返回如下響應(yīng):
ERR?Background?append?only?file?rewriting?already?in?progress
如果當(dāng)前進(jìn)程正在執(zhí)行bgsave操作,重寫命令延遲到bgsave完成之后再執(zhí)行,返回如下響應(yīng):
Background?append?only?file?rewriting?scheduled
2)父進(jìn)程執(zhí)行fork創(chuàng)建子進(jìn)程,開銷等同于bgsave過(guò)程。
3.1)主進(jìn)程fork操作完成后,繼續(xù)響應(yīng)其他命令。所有修改命令依然寫入AOF緩沖區(qū)并根據(jù)appendfsync策略同步到硬盤,保證原有AOF機(jī)制正確性。
3.2)由于fork操作運(yùn)用寫時(shí)復(fù)制技術(shù),子進(jìn)程只能共享fork操作時(shí)的內(nèi)存數(shù)據(jù)。由于父進(jìn)程依然響應(yīng)命令,Redis使用“AOF重寫緩沖區(qū)”保存這部分新數(shù)據(jù),防止新AOF文件生成期間丟失這部分?jǐn)?shù)據(jù)。
4)子進(jìn)程根據(jù)內(nèi)存快照,按照命令合并規(guī)則寫入到新的AOF文件。每次批量寫入硬盤數(shù)據(jù)量由配置aof-rewrite-incremental-fsync控制,默認(rèn)為32MB,防止單次刷盤數(shù)據(jù)過(guò)多造成硬盤阻塞。
5.1)新AOF文件寫入完成后,子進(jìn)程發(fā)送信號(hào)給父進(jìn)程,父進(jìn)程更新統(tǒng)計(jì)信息,具體見(jiàn)info persistence下的aof_*相關(guān)統(tǒng)計(jì)。
5.2)父進(jìn)程把AOF重寫緩沖區(qū)的數(shù)據(jù)寫入到新的AOF文件。
5.3)使用新AOF文件替換老文件,完成AOF重寫。
重啟加載
AOF和RDB文件都可以用于服務(wù)器重啟時(shí)的數(shù)據(jù)恢復(fù)。Redis持久化文件加載流程如下圖:

流程說(shuō)明:
1)AOF持久化開啟且存在AOF文件時(shí),優(yōu)先加載AOF文件,打印如下日志:
*?DB?loaded?from?append?only?file:?5.841?seconds
2)AOF關(guān)閉或者AOF文件不存在時(shí),加載RDB文件,打印如下日志:
*?DB?loaded?from?disk:?5.586?seconds
3)加載AOF/RDB文件成功后,Redis啟動(dòng)成功。
4)AOF/RDB文件存在錯(cuò)誤時(shí),Redis啟動(dòng)失敗并打印錯(cuò)誤信息。
文件校驗(yàn)
加載損壞的AOF文件時(shí)會(huì)拒絕啟動(dòng),并打印如下日志:
#?Bad?file?format?reading?the?append?only?file:?make?a?backup?of?your?AOF?file,then?use?./redis-check-aof?--fix
對(duì)于錯(cuò)誤格式的AOF文件,先進(jìn)行備份,然后采用redis-check-aof--fix命令進(jìn)行修復(fù),修復(fù)后使用diff-u對(duì)比數(shù)據(jù)的差異,找出丟失的數(shù)據(jù),有些可以人工修改補(bǔ)全。
AOF文件可能存在結(jié)尾不完整的情況,Redis為我們提供了aof-load-truncated配置來(lái)兼容這種情況,默認(rèn)開啟。加載AOF時(shí),當(dāng)遇到此問(wèn)題時(shí)會(huì)忽略并繼續(xù)啟動(dòng),同時(shí)打印如下警告日志:
#?!!!?Warning:?short?read?while?loading?the?AOF?file?!!! #?!!!?Truncating?the?AOF?at?offset?397856725?!!! #?AOF?loaded?anyway?because?aof-load-truncated?is?enabled