Redis 持久化的這些細(xì)節(jié),你真廢了嗎
Redis 的數(shù)據(jù)全部在內(nèi)存里,如果突然宕機(jī),數(shù)據(jù)就會全部丟失,因此必須有一種機(jī)制來保證 Redis 的數(shù)據(jù)不會因?yàn)楣收隙鴣G失,這種機(jī)制就是 Redis 的持久化機(jī)制,它會將內(nèi)存中的數(shù)據(jù)庫狀態(tài) 保存到磁盤 中。
Redis 有兩種持久化的方式:快照(RDB文件)和追加式文件(AOF文件)
RDB(Redis DataBase)
是什么
在指定的時間間隔內(nèi)將內(nèi)存中的所有數(shù)據(jù)集快照寫入磁盤,也就是行話講的 Snapshot 快照,它執(zhí)行的是全量快照,它恢復(fù)時是將快照文件直接讀到內(nèi)存里。
這就類似于照片,當(dāng)你拍照時,一張照片就能把拍照那一瞬間的形象完全記下來。
Redis 會單獨(dú)創(chuàng)建(fork)一個子進(jìn)程來進(jìn)行持久化,會先將數(shù)據(jù)寫入到一個臨時文件中,待持久化過程都結(jié)束了,再用這個臨時文件替換上次持久化好的文件。整個過程中,主進(jìn)程是不進(jìn)行任何 IO 操作的,這就確保了極高的性能,如果需要進(jìn)行大規(guī)模數(shù)據(jù)的恢復(fù),且對于數(shù)據(jù)恢復(fù)的完整性不是非常敏感,那 RDB 方式是比較高效的。
RDB 的缺點(diǎn)是最后一次持久化后的數(shù)據(jù)可能丟失。
? What ? Redis 不是單進(jìn)程的嗎?
Redis 使用操作系統(tǒng)的多進(jìn)程 COW(Copy On Write) 機(jī)制來實(shí)現(xiàn)快照持久化(在執(zhí)行快照的同時,正常處理寫操作), fork 是類 Unix 操作系統(tǒng)上創(chuàng)建進(jìn)程的主要方法。COW(Copy On Write)是計(jì)算機(jī)編程中使用的一種優(yōu)化策略。
fork 的作用是復(fù)制一個與當(dāng)前進(jìn)程一樣的進(jìn)程。新進(jìn)程的所有數(shù)據(jù)(變量、環(huán)境變量、程序計(jì)數(shù)器等)數(shù)值都和原進(jìn)程一致,但是是一個全新的進(jìn)程,并作為原進(jìn)程的子進(jìn)程。子進(jìn)程讀取數(shù)據(jù),然后序列化寫到磁盤中。
配置
配置位置:SNAPSHOTTING

rdb 默認(rèn)保存的是 dump.rdb 文件,如下(不可讀)

你可以對 Redis 進(jìn)行設(shè)置, 讓它在“ N 秒內(nèi)數(shù)據(jù)集至少有 M 個改動”這一條件被滿足時, 自動保存一次數(shù)據(jù)集。
比如說, 以下設(shè)置會讓 Redis 在滿足“ 60 秒內(nèi)有至少有 1000 個鍵被改動”這一條件時, 自動保存一次數(shù)據(jù)集:
save 60 1000
觸發(fā) RDB 快照
除了通過配置文件的方式,自動觸發(fā)生成快照,也可以使用命令手動觸發(fā)
save:save 時只管保存,在主線程中執(zhí)行,會導(dǎo)致阻塞,所以請慎用; bgsave:可以理解為 background save,當(dāng)執(zhí)行 bgsave 命令時,redis 會 fork 出一個子進(jìn)程,專門用于寫入 RDB 文件,避免了主線程的阻塞,這也是 Redis RDB 文件生成的默認(rèn)配置??梢酝ㄟ^lastsave命令獲取最后一次成功執(zhí)行快照的時間執(zhí)行 flushall 命令,也會產(chǎn)生 dump.rdb 文件,但里面是空的,無意義 客戶端執(zhí)行 shutdown 關(guān)閉 redis 時,也會觸發(fā)快照
簡單來說,bgsave 子進(jìn)程是由主線程 fork 生成的,可以共享主線程的所有內(nèi)存數(shù)據(jù)。bgsave 子進(jìn)程運(yùn)行后,開始讀取主線程的內(nèi)存數(shù)據(jù),并把它們寫入 RDB 文件。此時,如果主線程對這些數(shù)據(jù)也都是讀操作(例如圖中的鍵值對K1),那么,主線程和 bgsave 子進(jìn)程相互不影響。但是,如果主線程要修改一塊數(shù)據(jù)(例如圖中的鍵值對 K3),那么,這塊數(shù)據(jù)就會被復(fù)制一份,生成該數(shù)據(jù)的副本。然后,bgsave 子進(jìn)程會把這個副本數(shù)據(jù)寫入 RDB 文件,而在這個過程中,主線程仍然可以直接修改原來的數(shù)據(jù)。
redis-bgsave
快照的運(yùn)作方式
當(dāng) Redis 需要保存 dump.rdb 文件時, 服務(wù)器執(zhí)行以下操作:
Redis 調(diào)用 fork(),產(chǎn)生一個子進(jìn)程,此時同時擁有父進(jìn)程和子進(jìn)程。父進(jìn)程繼續(xù)處理 client 請求,子進(jìn)程負(fù)責(zé)將內(nèi)存內(nèi)容寫入到臨時文件。由于 os 的寫時復(fù)制機(jī)制,父子進(jìn)程會共享相同的物理頁面,當(dāng)父進(jìn)程處理寫請求時, os 會為父進(jìn)程要修改的頁面創(chuàng)建副本,而不是寫共享的頁面。所以子進(jìn)程的地址空間內(nèi)的數(shù)據(jù)是 fork 時刻整個數(shù)據(jù)庫的一個快照。 當(dāng)子進(jìn)程完成對新 RDB 文件的寫入時,Redis 用新 RDB 文件替換原來的 RDB 文件,并刪除舊的 RDB 文件。
這種工作方式使得 Redis 可以從寫時復(fù)制(copy-on-write)機(jī)制中獲益。
如何恢復(fù)
將備份文件 (dump.rdb) 移動到 Redis 安裝目錄并啟動服務(wù)即可(CONFIG GET dir 獲取目錄)

優(yōu)勢
一旦采用該方式,那么你的整個 Redis 數(shù)據(jù)庫將只包含一個文件,這對于文件備份而言是非常完美的。比如,你可能打算每個小時歸檔一次最近 24 小時的數(shù)據(jù),同時還要每天歸檔一次最近 30 天的數(shù)據(jù)。通過這樣的備份策略,一旦系統(tǒng)出現(xiàn)災(zāi)難性故障,我們可以非常容易的進(jìn)行恢復(fù)。適合大規(guī)模的數(shù)據(jù)恢復(fù) 對于災(zāi)難恢復(fù)而言,RDB 是非常不錯的選擇。因?yàn)槲覀兛梢苑浅]p松的將一個單獨(dú)的文件壓縮后再轉(zhuǎn)移到其它存儲介質(zhì)上。 性能最大化。對于 Redis 的服務(wù)進(jìn)程而言,在開始持久化時,它唯一需要做的只是 fork 出子進(jìn)程,之后再由子進(jìn)程完成這些持久化的工作,這樣就可以極大的避免服務(wù)進(jìn)程執(zhí)行 IO 操作了。
劣勢
如果你想保證數(shù)據(jù)的高可用性,即最大限度的避免數(shù)據(jù)丟失,那么 RDB 不是一個很好的選擇。因?yàn)橄到y(tǒng)一旦在定時持久化之前出現(xiàn)宕機(jī)現(xiàn)象,此前沒有來得及寫入磁盤的數(shù)據(jù)都將丟失(丟失最后一次快照后的所有修改)。 由于 RDB 是通過 fork 子進(jìn)程來協(xié)助完成數(shù)據(jù)持久化工作的,內(nèi)存中的數(shù)據(jù)被克隆了一份,大致 2 倍的膨脹性需要考慮,因此,如果當(dāng)數(shù)據(jù)集較大時,可能會導(dǎo)致整個服務(wù)器停止服務(wù)幾百毫秒,甚至是 1 秒鐘。
可能你也會和我有同樣的疑問,反正是不阻塞的,我每秒做一次快照,不就可以最大限度的避免數(shù)據(jù)丟失了嗎?
一方面,頻繁將全量數(shù)據(jù)寫入磁盤,會給磁盤帶來很大壓力,多個快照競爭有限的磁盤帶寬,前一個快照還沒有做完,后一個又開始做了,容易造成惡性循環(huán)。
另一方面,bgsave 子進(jìn)程需要通過 fork 操作從主線程創(chuàng)建出來。雖然,子進(jìn)程在創(chuàng)建后不會再阻塞主線程,但是,fork 這個創(chuàng)建過程本身會阻塞主線程,而且主線程的內(nèi)存越大,阻塞時間越長。如果頻繁 fork 出 bgsave 子進(jìn)程,這就會頻繁阻塞主線程了。
如何停止
動態(tài)停止 RDB 保存規(guī)則的方法:redis-cli config set save "",或者修改配置文件,重啟即可。
小總結(jié)

RDB 是一個非常緊湊的文件
RDB 在保存 RDB 文件時父進(jìn)程唯一需要做的就是 fork 出一個子進(jìn)程,接下來的工作全部由子進(jìn)程來做,父進(jìn)程不需要再做其他 IO 操作,所以 RDB 持久化方式可以最大化 Redis 的性能
數(shù)據(jù)丟失風(fēng)險大
RDB 需要經(jīng)常 fork 子進(jìn)程來保存數(shù)據(jù)集到硬盤上,當(dāng)數(shù)據(jù)集比較大的時候,fork 的過程是非常耗時的,可能會導(dǎo)致 Redis 在一些毫秒級不能響應(yīng)客戶端的請求
AOF(Append Only File)
是什么
以日志的形式來記錄每個寫操作,將 Redis 執(zhí)行過的所有寫指令記錄下來(讀操作不記錄),只許追加文件但不可以改寫文件,Redis 啟動之初會讀取該文件重新構(gòu)建數(shù)據(jù),也就是「重放」。換言之,Redis 重啟的話就根據(jù)日志文件的內(nèi)容將寫指令從前到后執(zhí)行一次以完成數(shù)據(jù)的恢復(fù)工作。
配置
AOF 默認(rèn)保存的是 **appendonly.aof ** 文件
配置位置:APPEND ONLY MODE

AOF 啟動/修復(fù)/恢復(fù)
正?;謴?fù)
啟動:修改默認(rèn)的 appendonly no,改為 yes 將有數(shù)據(jù)的 aof 文件復(fù)制一份保存到對應(yīng)目錄( config get dir)恢復(fù):重啟 redis 然后重新加載 異常恢復(fù)
啟動:修改默認(rèn)的 appendonly no,改為 yes 備份被寫壞的 AOF 文件 修復(fù):redis-check-aof --fix 進(jìn)行修復(fù) + AOF文件 恢復(fù):重啟 redis 然后重新加載
AOF 日志是如何實(shí)現(xiàn)的?
說到日志,我們比較熟悉的是數(shù)據(jù)庫的寫前日志(Write Ahead Log, WAL),也就是說,在實(shí)際寫數(shù)據(jù)前,先把修改的數(shù)據(jù)記到日志文件中,以便故障時進(jìn)行恢復(fù)(DBA 們常說的“日志先行”)。
不過,AOF 日志正好相反,它是寫后日志,“寫后”的意思是 Redis 是先執(zhí)行命令,把數(shù)據(jù)寫入內(nèi)存,然后才記錄日志,如下圖所示:

Tip:日志先行的方式,如果宕機(jī)后,還可以通過之前保存的日志恢復(fù)到之前的數(shù)據(jù)狀態(tài)??墒?AOF 后寫日志的方式,如果宕機(jī)后,不就會把寫入到內(nèi)存的數(shù)據(jù)丟失嗎?
那 AOF 為什么要先執(zhí)行命令再記日志呢?要回答這個問題,我們要先知道 AOF 里記錄了什么內(nèi)容。
傳統(tǒng)數(shù)據(jù)庫的日志,例如 redo log(重做日志),記錄的是修改后的數(shù)據(jù),而 AOF 里記錄的是 Redis 收到的每一條命令,這些命令是以文本形式保存的。
我們以 Redis 收到 “set k1 v1” 命令后記錄的日志為例,看看 AOF 日志的內(nèi)容。其中,“*2” 表示當(dāng)前命令有兩個部分,每部分都是由 “$+數(shù)字”開頭,后面緊跟著具體的命令、鍵或值。這里,“數(shù)字”表示這部分中的命令、鍵或值一共有多少字節(jié)。
例如,*2 表示有兩個部分,$6 表示 6 個字節(jié),也就是下邊的 “SELECT” 命令,$1 表示 1 個字節(jié),也就是下邊的 “0” 命令,合起來就是 SELECT 0,選擇 0 庫。下邊的指令同理,就很好理解了 SET K1 V1。

但是,為了避免額外的檢查開銷,Redis 在向 AOF 里面記錄日志的時候,并不會先去對這些命令進(jìn)行語法檢查。所以,如果先記日志再執(zhí)行命令的話,日志中就有可能記錄了錯誤的命令,Redis 在使用日志恢復(fù)數(shù)據(jù)時,就可能會出錯。而寫后日志這種方式,就是先讓系統(tǒng)執(zhí)行命令,只有命令能執(zhí)行成功,才會被記錄到日志中,否則,系統(tǒng)就會直接向客戶端報錯。所以,Redis 使用寫后日志這一方式的一大好處是,可以避免出現(xiàn)記錄錯誤命令的情況。除此之外,AOF 還有一個好處:它是在命令執(zhí)行后才記錄日志,所以不會阻塞當(dāng)前的寫操作。
不過,AOF 也有兩個潛在的風(fēng)險。
首先,如果剛執(zhí)行完一個命令,還沒有來得及記日志就宕機(jī)了,那么這個命令和相應(yīng)的數(shù)據(jù)就有丟失的風(fēng)險。如果此時 Redis 是用作緩存,還可以從后端數(shù)據(jù)庫重新讀入數(shù)據(jù)進(jìn)行恢復(fù),但是,如果 Redis 是直接用作數(shù)據(jù)庫的話,此時,因?yàn)槊顩]有記入日志,所以就無法用日志進(jìn)行恢復(fù)了。
其次,AOF 雖然避免了對當(dāng)前命令的阻塞,但可能會給下一個操作帶來阻塞風(fēng)險。這是因?yàn)?,AOF 日志也是在主線程中執(zhí)行的,如果在把日志文件寫入磁盤時,磁盤寫壓力大,就會導(dǎo)致寫盤很慢,進(jìn)而導(dǎo)致后續(xù)的操作也無法執(zhí)行了。
仔細(xì)分析的話,你就會發(fā)現(xiàn),這兩個風(fēng)險都是和 AOF 寫回磁盤的時機(jī)相關(guān)的。這也就意味著,如果我們能夠控制一個寫命令執(zhí)行完后 AOF 日志寫回磁盤的時機(jī),這兩個風(fēng)險就解除了。
接著,我們就看下 Redis 提供的寫回策略,或者叫 AOF 耐久性。
三種寫回策略 | AOF 耐久性
你可以配置 Redis 多久才將數(shù)據(jù) fsync 到磁盤一次。AOF 機(jī)制給我們提供了三個選擇:
appendfsync always,同步寫回:每個寫命令執(zhí)行完,立馬同步地將日志寫回磁盤;
appendfsync everysec,每秒寫回:每個寫命令執(zhí)行完,只是先把日志寫到 AOF 文件的內(nèi)存緩沖區(qū),每隔一秒把緩沖區(qū)中的內(nèi)容寫入磁盤;
但是當(dāng)這一次的 fsync 調(diào)用時長超過 1 秒時。Redis 會采取延遲 fsync 的策略,再等一秒鐘。也就是在兩秒后再進(jìn)行 fsync,這一次的 fsync 就不管會執(zhí)行多長時間都會進(jìn)行。這時候由于在 fsync 時文件描述符會被阻塞,所以當(dāng)前的寫操作就會阻塞。
appendfsync no,操作系統(tǒng)控制的寫回:每個寫命令執(zhí)行完,只是先把日志寫到 AOF 文件的內(nèi)存緩沖區(qū),由操作系統(tǒng)決定何時將緩沖區(qū)內(nèi)容寫回磁盤。對大多數(shù) Linux 操作系統(tǒng),是每 30 秒進(jìn)行一次 fsync,將緩沖區(qū)中的數(shù)據(jù)寫到磁盤上。
針對避免主線程阻塞和減少數(shù)據(jù)丟失問題,這三種寫回策略都無法做到兩全其美。我們來分析下其中的原因。
“同步寫回”可以做到基本不丟數(shù)據(jù),但是它在每一個寫命令后都有一個慢速的落盤操作,不可避免地會影響主線程性能; 雖然“操作系統(tǒng)控制的寫回”在寫完緩沖區(qū)后,就可以繼續(xù)執(zhí)行后續(xù)的命令,但是落盤的時機(jī)已經(jīng)不在 Redis 手中了,只要 AOF 記錄沒有寫回磁盤,一旦宕機(jī)對應(yīng)的數(shù)據(jù)就丟失了; “每秒寫回”采用一秒寫回一次的頻率,避免了“同步寫回”的性能開銷,雖然減少了對系統(tǒng)性能的影響,但是如果發(fā)生宕機(jī),上一秒內(nèi)未落盤的命令操作仍然會丟失。所以,這只能算是,在避免影響主線程性能和避免數(shù)據(jù)丟失兩者間取了個折中。
| 配置項(xiàng) | 寫回時機(jī) | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| Always | 同步寫回 | 可靠性高,數(shù)據(jù)基本不丟失 | 每個寫命令都要落盤,性能影響較大,慢但是安全 |
| Everysec | 每秒寫回 | 性能適中 | 宕機(jī)時丟失1秒內(nèi)的數(shù)據(jù) |
| No | 操作系統(tǒng)控制的寫回 | 性能好 | 宕機(jī)時丟失數(shù)據(jù)較多 |
到這里,我們就可以根據(jù)系統(tǒng)對高性能和高可靠性的要求,來選擇使用哪種寫回策略了。
總結(jié)一下就是:想要獲得高性能,就選擇 No 策略;如果想要得到高可靠性保證,就選擇 Always 策略;如果允許數(shù)據(jù)有一點(diǎn)丟失,又希望性能別受太大影響的話,那么就選擇 Everysec 策略。
Tip:試想一下,如果我們開啟 AOF 運(yùn)行個一年半載的,AOF 文件是不是會越來越大,先不說占用資源的問題,如果宕機(jī)重啟,以 AOF 文件重做數(shù)據(jù),肯定是個特別漫長的過程,所以 Redis 提供了對 AOF 文件的“瘦身”機(jī)制。
rewrite(AOF 重寫)
是什么:AOF 采用文件追加方式,文件會越來越大,為了避免出現(xiàn)這種情況,新增了重寫機(jī)制,當(dāng) AOF 文件的大小超過所設(shè)定的閾值時,Redis 就會啟動 AOF 文件的內(nèi)容壓縮,只保留可以恢復(fù)數(shù)據(jù)的最小指令集,可以使用命令
bgrewriteaof,這個操作相當(dāng)于對 AOF 文件“瘦身”。在重寫的時候,是根據(jù)這個鍵值對當(dāng)前的最新狀態(tài),為它生成對應(yīng)的寫入命令。這樣一來,一個鍵值對在重寫日志中只用一條命令就行了,而且,在日志恢復(fù)時,只用執(zhí)行這條命令,就可以直接完成這個鍵值對的寫入了。
減肥 重寫原理:AOF 文件持續(xù)增長而過大時,會 fork 出一條新進(jìn)程來將文件重寫(也是先寫臨時文件最后再rename),遍歷新進(jìn)程的內(nèi)存中數(shù)據(jù),轉(zhuǎn)換成一條條的操作指令,再序列化到一個新的 AOF 文件中。
PS:重寫 AOF 文件的操作,并沒有讀取舊的 AOF 文件,而是將整個內(nèi)存中的數(shù)據(jù)庫內(nèi)容用命令的方式重寫了一個新的 AOF 文件,這點(diǎn)和快照有點(diǎn)類似。
觸發(fā)機(jī)制:Redis 會記錄上次重寫時的 AOF 大小,默認(rèn)配置是當(dāng) AOF 文件大小是上次 rewrite 后大小的一倍且文件大于 64M 時觸發(fā)
我們在客戶端輸入兩次
set k1 v1,然后比較bgrewriteaof前后兩次的 appendonly.aof 文件(先要關(guān)閉混合持久化)

如果 AOF 文件出錯了,怎么辦?
服務(wù)器可能在程序正在對 AOF 文件進(jìn)行寫入時停機(jī), 如果停機(jī)造成了 AOF 文件出錯(corrupt), 那么 Redis 在重啟時會拒絕載入這個 AOF 文件, 從而確保數(shù)據(jù)的一致性不會被破壞。
當(dāng)發(fā)生這種情況時, 可以用以下方法來修復(fù)出錯的 AOF 文件:
為現(xiàn)有的 AOF 文件創(chuàng)建一個備份。 使用 Redis 附帶的 redis-check-aof 程序,對原來的 AOF 文件進(jìn)行修復(fù)。
$ redis-check-aof --fix
(可選)使用 diff -u 對比修復(fù)后的 AOF 文件和原始 AOF 文件的備份,查看兩個文件之間的不同之處。 重啟 Redis 服務(wù)器,等待服務(wù)器載入修復(fù)后的 AOF 文件,并進(jìn)行數(shù)據(jù)恢復(fù)。
AOF 運(yùn)作方式 | 后臺重寫
AOF 重寫和 RDB 創(chuàng)建快照一樣,都巧妙地利用了寫時復(fù)制機(jī)制。
不過, 使用子進(jìn)程也有一個問題需要解決:因?yàn)樽舆M(jìn)程在進(jìn)行 AOF 重寫期間, 主進(jìn)程還需要繼續(xù)處理命令, 而新的命令可能對現(xiàn)有的數(shù)據(jù)進(jìn)行修改, 這會讓當(dāng)前數(shù)據(jù)庫的數(shù)據(jù)和重寫后的 AOF 文件中的數(shù)據(jù)不一致。
為了解決這個問題, Redis 增加了一個 AOF 重寫緩存, 這個緩存在 fork 出子進(jìn)程之后開始啟用, Redis 主進(jìn)程在接到新的寫命令之后, 除了會將這個寫命令的協(xié)議內(nèi)容追加到現(xiàn)有的 AOF 文件之外, 還會追加到這個緩存中:
以下是 AOF 重寫的執(zhí)行步驟:
Redis 執(zhí)行 fork(),現(xiàn)在同時擁有父進(jìn)程和子進(jìn)程。子進(jìn)程開始將新 AOF 文件的內(nèi)容寫入到臨時文件。 對于所有新執(zhí)行的寫入命令,父進(jìn)程一邊將它們累積到一個內(nèi)存緩存中,一邊將這些改動追加到現(xiàn)有 AOF 文件的末尾:這樣即使在重寫的中途發(fā)生停機(jī),現(xiàn)有的 AOF 文件也還是安全的。 當(dāng)子進(jìn)程完成重寫工作時,它給父進(jìn)程發(fā)送一個信號,父進(jìn)程在接收到信號之后,將內(nèi)存緩存中的所有數(shù)據(jù)追加到新 AOF 文件的末尾。 搞定!現(xiàn)在 Redis 原子地用新文件替換舊文件,之后所有命令都會直接追加到新 AOF 文件的末尾。

優(yōu)勢
該機(jī)制可以帶來更高的數(shù)據(jù)安全性,即數(shù)據(jù)持久性。Redis 中提供了 3 種同步策略,即每秒同步、每修改同步和不同步。事實(shí)上,每秒同步也是異步完成的,其效率也是非常高的,所差的是一旦系統(tǒng)出現(xiàn)宕機(jī)現(xiàn)象,那么這一秒鐘之內(nèi)修改的數(shù)據(jù)將會丟失。而每修改同步,我們可以將其視為同步持久化,即每次發(fā)生的數(shù)據(jù)變化都會被立即記錄到磁盤中??梢灶A(yù)見,這種方式在效率上是最低的。至于無同步,無需多言,我想大家都能正確的理解它。 由于該機(jī)制對日志文件的寫入操作采用的是 append 模式,因此在寫入過程中即使出現(xiàn)宕機(jī)現(xiàn)象,也不會破壞日志文件中已經(jīng)存在的內(nèi)容。然而如果我們本次操作只是寫入了一半數(shù)據(jù)就出現(xiàn)了系統(tǒng)崩潰問題,不用擔(dān)心,在 Redis 下一次啟動之前,我們可以通過 redis-check-aof 工具來幫助我們解決數(shù)據(jù)一致性的問題。 如果日志過大,Redis 可以自動啟用 rewrite 機(jī)制。即 Redis 以 append 模式不斷的將修改數(shù)據(jù)寫入到老的磁盤文件中,同時 Redis 還會創(chuàng)建一個新的文件用于記錄此期間有哪些修改命令被執(zhí)行。因此在進(jìn)行 rewrite 切換時可以更好的保證數(shù)據(jù)安全性。 AOF 包含一個格式清晰、易于理解的日志文件用于記錄所有的修改操作。事實(shí)上,我們也可以通過該文件完成數(shù)據(jù)的重建。因此 AOF 文件的內(nèi)容非常容易被人讀懂, 對文件進(jìn)行分析(parse)也很輕松。導(dǎo)出(export) AOF 文件也非常簡單:舉個例子, 如果你不小心執(zhí)行了 FLUSHALL 命令, 但只要 AOF 文件未被重寫, 那么只要停止服務(wù)器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重啟 Redis , 就可以將數(shù)據(jù)集恢復(fù)到 FLUSHALL 執(zhí)行之前的狀態(tài)。
劣勢
對于相同數(shù)量的數(shù)據(jù)集而言,AOF文件通常要大于RDB文件。恢復(fù)速度慢于rdb。 根據(jù)同步策略的不同,AOF在運(yùn)行效率上往往會慢于RDB??傊?,每秒同步策略的效率是比較高的,同步禁用策略的效率和RDB一樣高效。
總結(jié)

AOF 文件是一個只進(jìn)行追加的日志文件 Redis 可以在 AOF 文件體積變得過大時,自動在后臺對 AOF 進(jìn)行重寫 AOF 文件有序的保存了對數(shù)據(jù)庫執(zhí)行的所有寫入操作,這些寫入操作以 Redis 協(xié)議的格式保存,因此 AOF 文件的內(nèi)容非常容易被人讀懂,對文件進(jìn)行分析也很輕松 對于相同的數(shù)據(jù)集來說,AOF 文件的體積通常需要大于 RDB 文件的體積 根據(jù)所使用的 fsync 策略,AOF 的速度可能會慢于 RDB
怎么從 RDB 持久化切換到 AOF 持久化
在 Redis 2.2 或以上版本,可以在不重啟的情況下,從 RDB 切換到 AOF :
為最新的 dump.rdb 文件創(chuàng)建一個備份。
將備份放到一個安全的地方。
執(zhí)行以下兩條命令:
redis-cli> CONFIG SET appendonly yes
redis-cli> CONFIG SET save ""確保命令執(zhí)行之后,數(shù)據(jù)庫的鍵的數(shù)量沒有改變。
確保寫命令會被正確地追加到 AOF 文件的末尾。
步驟 3 執(zhí)行的第一條命令開啟了 AOF 功能:Redis 會阻塞直到初始 AOF 文件創(chuàng)建完成為止, 之后 Redis 會繼續(xù)處理命令請求, 并開始將寫入命令追加到 AOF 文件末尾。
步驟 3 執(zhí)行的第二條命令用于關(guān)閉 RDB 功能。這一步是可選的, 如果你愿意的話, 也可以同時使用 RDB 和 AOF 這兩種持久化功能。
別忘了在 redis.conf 中打開 AOF 功能!否則的話, 服務(wù)器重啟之后, 之前通過 CONFIG SET 設(shè)置的配置就會被遺忘, 程序會按原來的配置來啟動服務(wù)器。
Which one
RDB 持久化方式能夠在指定的時間間隔能對你的數(shù)據(jù)進(jìn)行快照存儲
AOF 持久化方式記錄每次對服務(wù)器寫的操作,當(dāng)服務(wù)器重啟的時候會重新執(zhí)行這些命令來恢復(fù)原始的數(shù)據(jù),AOF 命令以 Redis 協(xié)議追加保存每次寫的操作到文件末尾。Redis 還能對 AOF 文件進(jìn)行后臺重寫(bgrewriteaof),使得 AOF 文件的體積不至于過大
只做緩存:如果你只希望你的數(shù)據(jù)在服務(wù)器運(yùn)行的時候存在,你也可以不使用任何持久化方式。
同時開啟兩種持久化方式
在這種情況下,當(dāng) Redis 重啟的時候會優(yōu)先載入 AOF 文件來恢復(fù)原始的數(shù)據(jù),因?yàn)樵谕ǔG闆r下 AOF 文件保存的數(shù)據(jù)集要比 RDB 文件保存的數(shù)據(jù)集要完整。 RDB 的數(shù)據(jù)不實(shí)時,同時使用兩者時服務(wù)器重啟也只會找 AOF 文件。那要不要只使用AOF 呢?建議不要,因?yàn)?RDB 更適合用于備份數(shù)據(jù)庫(AOF 在不斷變化不好備份),快速重啟,而且不會有 AOF 可能潛在的 bug,留著作為一個萬一的手段。
性能建議
因?yàn)?RDB 文件只用作后備用途,建議只在 Slave上持久化 RDB 文件,而且只要 15 分鐘備份一次就夠了,只保留 save 900 1這條規(guī)則。如果 Enalbe AOF,好處是在最惡劣情況下也只會丟失不超過兩秒數(shù)據(jù),啟動腳本較簡單只 load 自己的 AOF 文件就可以了。代價一是帶來了持續(xù)的 IO,二是 AOF rewrite 的最后將 rewrite 過程中產(chǎn)生的新數(shù)據(jù)寫到新文件造成的阻塞幾乎是不可避免的。只要硬盤許可,應(yīng)該盡量減少 AOF rewrite 的頻率,AOF 重寫的基礎(chǔ)大小默認(rèn)值 64M 太小了,可以設(shè)到 5G 以上。默認(rèn)超過原大小 100% 大小時重寫可以改到適當(dāng)?shù)臄?shù)值。 如果不 Enable AOF ,僅靠 Master-Slave Replication 實(shí)現(xiàn)高可用性也可以。能省掉一大筆 IO ,也減少了rewrite 時帶來的系統(tǒng)波動。代價是如果 Master/Slav e同時宕掉,會丟失十幾分鐘的數(shù)據(jù),啟動腳本也要比較兩個 Master/Slave 中的 RDB 文件,載入較新的那個。
Redis 4.0 混合持久化
Redis 4.0 中提出了一個混合使用 AOF 日志和內(nèi)存快照的方法。簡單來說,內(nèi)存快照以一定的頻率執(zhí)行,在兩次快照之間,使用 AOF 日志記錄這期間的所有命令操作。也就是將 RDB 文件的內(nèi)容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是自持久化開始到持久化結(jié)束的這段時間發(fā)生的增量 AOF 日志。
同樣我們執(zhí)行 3 次 set k1 v1,然后手動瘦身 bgrewriteaof 后,查看 appendonly.aof 文件:

這樣做的好處是可以結(jié)合 rdb 和 aof 的優(yōu)點(diǎn),快速加載同時避免丟失過多的數(shù)據(jù),缺點(diǎn)是 aof 里面的 rdb 部分就是壓縮格式不再是 aof 格式,可讀性差。
這樣一來,快照不用很頻繁地執(zhí)行,這就避免了頻繁 fork 對主線程的影響。而且,AOF 日志也只用記錄兩次快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現(xiàn)文件過大的情況了,也可以避免重寫開銷。
4.0 版本的混合持久化功能 默認(rèn)關(guān)閉,我們可以通過 aof-use-rdb-preamble 配置參數(shù)控制該功能的啟用。5.0 版本之后 默認(rèn)開啟。
如下圖所示,兩次快照中間時刻的修改,用 AOF 日志記錄,等到第二次做全量快照時,就可以清空 AOF 日志,因?yàn)榇藭r的修改都已經(jīng)記錄到快照中了,恢復(fù)時就不再用日志了。

這個方法既能享受到 RDB 文件快速恢復(fù)的好處,又能享受到 AOF 只記錄操作命令的簡單優(yōu)勢,有點(diǎn)“魚和熊掌可以兼得”的意思。
封面下載:公眾號回復(fù)“0304”
點(diǎn)擊關(guān)注公眾號↑↑


