Redis持久化使用及說(shuō)明(RDB&AOF)
Redis(數(shù)據(jù)存儲(chǔ)在內(nèi)存中)支持 RDB 和 AOF 兩種持久化(和 MySQL 里的持久性是一回事,把數(shù)據(jù)存儲(chǔ)在硬盤(pán)上,重啟進(jìn)程 / 主機(jī)后數(shù)據(jù)仍然存在 —— 持久;把數(shù)據(jù)存儲(chǔ)在內(nèi)存上,重啟進(jìn)程 / 主機(jī)后數(shù)據(jù)消失 —— 不持久)機(jī)制,持久化功能有效地避免因進(jìn)程退出造成數(shù)據(jù)丟失問(wèn)題,當(dāng)下次重啟時(shí)利用之前持久化的文件即可實(shí)現(xiàn)數(shù)據(jù)恢復(fù)。
Redis 為了保證速度快,數(shù)據(jù)肯定還得存儲(chǔ)在內(nèi)存中。但是為了持久化,數(shù)據(jù)還得想辦法存儲(chǔ)在硬盤(pán)上。所以,最后決定在內(nèi)存中和硬盤(pán)上都存儲(chǔ)數(shù)據(jù),這樣的兩份數(shù)據(jù)在理論上(實(shí)際上可能存在一個(gè)小的概率有差異,具體取決于如何進(jìn)行持久化)是完全相同的。當(dāng)要插入一個(gè)新的數(shù)據(jù)時(shí),就需要把這個(gè)數(shù)據(jù)同時(shí)寫(xiě)入到內(nèi)存和硬盤(pán)。當(dāng)查詢(xún)某個(gè)數(shù)據(jù)時(shí),直接從內(nèi)存中讀取,硬盤(pán)的數(shù)據(jù)只是在 Redis 重啟時(shí),用來(lái)恢復(fù)內(nèi)存中的數(shù)據(jù)。代價(jià)就是消耗了更多的空間,同一份數(shù)據(jù)存儲(chǔ)了兩遍,但畢竟硬盤(pán)價(jià)格便宜,開(kāi)銷(xiāo)不會(huì)帶來(lái)太大的成本。而且實(shí)際上具體怎么寫(xiě)硬盤(pán)還有不同的策略,是可以保證整體的效率足夠高的。
假設(shè)我自己的電腦上有很多的學(xué)習(xí)資料,雖然現(xiàn)在是把這些學(xué)習(xí)資料保存在硬盤(pán)上(持久化保存),但是萬(wàn)一我的電腦出現(xiàn)故障(相較于 CPU、顯卡、內(nèi)存來(lái)說(shuō),硬盤(pán)是最容易出問(wèn)題的,尤其是機(jī)械硬盤(pán))??梢阅昧硪粔K移動(dòng)硬盤(pán)來(lái)作為備份用的硬盤(pán):
- 定期備份(每個(gè)月將電腦硬盤(pán)上的學(xué)習(xí)資料整體的備份到這個(gè)備份盤(pán)中)—— RDB
- 實(shí)時(shí)備份(只要下載了一個(gè)新的學(xué)習(xí)資料,就立即把這份學(xué)習(xí)資料往備份盤(pán)中拷貝一份)—— AOF
一、RDB
RDB(Redis DataBase)持久化是把定期的將當(dāng)前的進(jìn)程數(shù)據(jù)生成快照保存到硬盤(pán)的過(guò)程,觸發(fā) RDB 持久化過(guò)程分為手動(dòng)觸發(fā)和自動(dòng)觸發(fā)。
1、觸發(fā)機(jī)制
手動(dòng)觸發(fā)分別對(duì)應(yīng) save 和 bgsave 命令:
- save 命令:阻塞當(dāng)前 Redis 服務(wù)器,直到 RDB 過(guò)程完成為止,對(duì)于內(nèi)存比較大的實(shí)例造成長(zhǎng)時(shí)間阻塞。(一半不建議使用)
- bgsave(background save)命令:Redis 進(jìn)程執(zhí)行 fork 操作創(chuàng)建子進(jìn)程,RDB 持久化過(guò)程由子進(jìn)程負(fù)責(zé),完成后自動(dòng)結(jié)束。阻塞只發(fā)生在 fork 階段,一般時(shí)間很短。不會(huì)影響 Redis 服務(wù)器處理其他客戶(hù)端的請(qǐng)求和命令。(此處 Redis 使用的是 “多進(jìn)程” 的方式來(lái)完成的并發(fā)編程,來(lái)完成 bgsave 的實(shí)現(xiàn))
Redis 內(nèi)部的所有涉及 RDB 的操作都采用類(lèi)似 bgsave 的方式。
如果插入新的 key,而此時(shí)不手動(dòng)執(zhí)行 bgsave,直接重新啟動(dòng) Redis 服務(wù)器,那么剛剛插入的數(shù)據(jù)在重啟之后仍然存在。所以說(shuō),Redis 生成快照操作不僅僅是手動(dòng)執(zhí)行命令才觸發(fā),也可以自動(dòng)觸發(fā),也就是下面的第 3 點(diǎn)。
除了手動(dòng)觸發(fā)之外,Redis 運(yùn)行自動(dòng)觸發(fā) RDB 持久化機(jī)制,這個(gè)觸發(fā)機(jī)制才是在實(shí)戰(zhàn)中有價(jià)值的。
- 使用 save 配置。如 "save m n" 表示 m 秒內(nèi)數(shù)據(jù)集發(fā)生了 n 次修改,自動(dòng) RDB 持久化。
- 從節(jié)點(diǎn)進(jìn)行全量復(fù)制(主從復(fù)制)操作時(shí),主節(jié)點(diǎn)自動(dòng)進(jìn)行 RDB 持久化生成快照,隨后將 RDB 快照文件內(nèi)容發(fā)送給從節(jié)點(diǎn)。
- 如果執(zhí)行 shutdown 命令(service redis-server restart,正常關(guān)閉)關(guān)閉 Redis 時(shí),或者通過(guò)正常流程重新啟動(dòng) Redis 服務(wù)器,那么此時(shí) Redis 服務(wù)器會(huì)在退出時(shí)自動(dòng)執(zhí)行 RDB 持久化。但如果是異常重啟(kill -9 或者服務(wù)器掉電),那么此時(shí) Redis 服務(wù)器來(lái)不及生成 rdb,內(nèi)存中尚未保存到快照中的數(shù)據(jù),就會(huì)隨著重啟而丟失。

并不是說(shuō) Redis 客戶(hù)端這邊插入了數(shù)據(jù),rdb 文件中的數(shù)據(jù)就會(huì)立即更新的。插入幾個(gè)鍵值對(duì)后,沒(méi)有運(yùn)行手動(dòng)觸發(fā)的命令,也達(dá)不到自動(dòng)觸發(fā)的條件,那么就不會(huì)更新。
如果修改成如下內(nèi)容:

對(duì)于 Redis 來(lái)說(shuō),配置文件發(fā)生修改后,一定要重新啟動(dòng)服務(wù)器才能生效。(當(dāng)然,如果想要立即生效,也可以通過(guò)命令的方式進(jìn)行修改)

同時(shí)滿(mǎn)足兩個(gè)條件即可觸發(fā)快照的生成:

如果是修改成以下內(nèi)容:

那么將會(huì)關(guān)閉自動(dòng)生成快照這個(gè)功能。
如果把 rdb 文件故意改壞會(huì)怎么樣?
手動(dòng)把 rdb 文件內(nèi)容改壞,如果是通過(guò) service redis-server restart 重啟,就會(huì)在 Redis 服務(wù)器退出時(shí)重新生成 rdb 快照,那么剛才改壞的文件就會(huì)被替換掉。
而如果是通過(guò) kill 進(jìn)程的方式,再重新啟動(dòng) Redis 服務(wù)器,此時(shí) rdb 文件就還是錯(cuò)的。但看起來(lái) Redis 好像沒(méi)有受到什么影響,還是能正常啟動(dòng),能正確獲取到 key。那是因?yàn)閯偛判薷牡奈恢脩?yīng)該正好是文件的末尾,對(duì)前面的內(nèi)容沒(méi)有什么影響。但如果是修改了中間位置的內(nèi)容,那么 Redis 服務(wù)器就啟動(dòng)不了了。
此時(shí),Redis 服務(wù)器掛了,可以看看 Redis 日志,了解一下發(fā)生了什么。

打開(kāi)該文件,可以看到:

也就是在 rdb 恢復(fù)數(shù)據(jù)的過(guò)程中出現(xiàn)了問(wèn)題。
rdb 文件是二進(jìn)制的,如果直接把壞了的 rdb 文件交給 Redis 服務(wù)器去使用,那么得到的結(jié)果是不可預(yù)期的。
所以,Redis 也提供了 rdb 文件的檢查工具,可以先通過(guò)檢查工具來(lái)檢查一下 rdb 文件格式是否符合要求。

5.0 版本中,檢查工具和 Redis 服務(wù)器是同一個(gè)可執(zhí)行程序,可以在運(yùn)行時(shí)加入不同的選項(xiàng),從而使用其中不同的功能。
運(yùn)行時(shí)加入 rdb 文件作為命令行參數(shù),那么此時(shí)就是以檢查工具的方式來(lái)運(yùn)行,不會(huì)真的啟動(dòng) Redis 服務(wù)器。

2、流程說(shuō)明
bgsave 是主流的 RDB 持久化方式,根據(jù)下圖來(lái)了解它的運(yùn)作流程。
bgsave 命令的運(yùn)作流程:

- 執(zhí)行 bgsave 命令,Redis 父進(jìn)程(Redis 服務(wù)器)判斷當(dāng)前進(jìn)是否存在其他正在執(zhí)行的子進(jìn)程,比如:RDB / AOF 子進(jìn)程,如果存在 bgsave 命令直接返回。
- 如果沒(méi)有其它的工作子進(jìn)程,父進(jìn)程通過(guò)執(zhí)行 fork 這樣的系統(tǒng)調(diào)用來(lái)創(chuàng)建一個(gè)子進(jìn)程(該場(chǎng)景中的絕大部分內(nèi)存數(shù)據(jù)是不需要改變的,所以在短時(shí)間內(nèi)父進(jìn)程中不會(huì)有大批的內(nèi)存數(shù)據(jù)變化,因此子進(jìn)程的 “寫(xiě)時(shí)拷貝” 并不會(huì)觸發(fā)很多次),fork 過(guò)程中父進(jìn)程會(huì)阻塞,通過(guò) info stats 命令查看 latest_fork_usec 選項(xiàng),可以獲取最近一次 fork 操作的耗時(shí),單位為微秒。
- 父進(jìn)程 fork 完成后,bgsave 命令返回 "Background saving started" 信息,并不再阻塞父進(jìn)程,可以繼續(xù)響應(yīng)其他命令。
- 子進(jìn)程創(chuàng)建 RDB 文件,根據(jù)父進(jìn)程內(nèi)存生成臨時(shí)快照文件,完成后對(duì)原有文件進(jìn)行原子替換。執(zhí)行 lastsave 命令可以獲取最后一次生成 RDB 的時(shí)間,對(duì)應(yīng) info 統(tǒng)計(jì)的 rdb_last_save_time 選項(xiàng)。
- 進(jìn)程發(fā)送信號(hào)給父進(jìn)程表示完成,父進(jìn)程更新統(tǒng)計(jì)信息,子進(jìn)程就可以結(jié)束銷(xiāo)毀了。
bgsave 操作流程是創(chuàng)建子進(jìn)程,子進(jìn)程完成持久化操作(持久化速度太快了(數(shù)據(jù)少),難以觀察到子進(jìn)程),持久化會(huì)把數(shù)據(jù)寫(xiě)入到新的文件中,然后使用新的文件替換舊的文件(這個(gè)容易觀察)。
可以通過(guò) Linux 的 stat 命令來(lái)查看文件的 inode 編號(hào):



這兩個(gè)文件不再是同一個(gè)文件了,只不過(guò)文件內(nèi)容是一樣的。inode 編號(hào)就相當(dāng)于文件的身份標(biāo)識(shí)。如果是直接使用 save 命令,那么此時(shí)是不會(huì)觸發(fā)子進(jìn)程和文件替換邏輯的,會(huì)直接在當(dāng)前進(jìn)程中往剛才的同一文件中寫(xiě)入數(shù)據(jù)。
文件系統(tǒng)典型的組織方式(ext4)主要是把整個(gè)文件系統(tǒng)分成三個(gè)大的部分:
- 超級(jí)塊(存放一些管理信息)
- inode 區(qū)(存放 inode 節(jié)點(diǎn),每個(gè)文件都會(huì)分配一個(gè) inode 數(shù)據(jù)結(jié)構(gòu),包含文件的各種元數(shù)據(jù))
- block 區(qū)(存放文件的數(shù)據(jù)內(nèi)容)
3、RDB 文件的處理
保存:RDB 文件(把內(nèi)存中的數(shù)據(jù)以壓縮(需要消耗一定的 cpu 資源,但是能節(jié)省存儲(chǔ)空間)的形式保存到這個(gè)二進(jìn)制文件中)保存在 dir 配置指定的目錄(默認(rèn) /var/lib/redis/)下,文件名通過(guò) dbfilename 配置(默認(rèn) dump.rdb)指定。
可以通過(guò)執(zhí)行 config set dir {newDir} 和 config set dbfilename {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)開(kāi)啟,可以通過(guò)參數(shù) config set rdbcompression {yes|no} 動(dòng)態(tài)修改。
雖然壓縮 RDB 會(huì)消耗 CPU,但可以大幅降低文件的體積,方便保存到硬盤(pán)或通過(guò)網(wǎng)絡(luò)發(fā)送到從節(jié)點(diǎn),因此建議開(kāi)啟。
- 校驗(yàn):如果 Redis 啟動(dòng)時(shí)加載到損壞的 RDB 文件會(huì)拒絕啟動(dòng)。這時(shí)可以使用 Redis 提供的 redis-check-dump 工具檢測(cè) RDB 文件并獲取對(duì)應(yīng)的錯(cuò)誤報(bào)告。
當(dāng)執(zhí)行生成 rdb 鏡像操作時(shí),此時(shí)就會(huì)把要生成的快照數(shù)據(jù)先保存到一個(gè)臨時(shí)文件中。當(dāng)這個(gè)快照生成完畢后,再刪除之前的 rdb 文件,把新生成的臨時(shí) rdb 文件名改成剛才的 dump.rdb。也就保證了,rdb 文件自始至終只有一個(gè)。
執(zhí)行 flushall 命令會(huì)自動(dòng)情況 rdb 文件。
4、RDB 的優(yōu)缺點(diǎn)
(1)優(yōu)點(diǎn)
- RDB 是?個(gè)緊湊壓縮的二進(jìn)制文件,代表 Redis 在某個(gè)時(shí)間點(diǎn)上的數(shù)據(jù)快照。非常適用于備份,全量復(fù)制等場(chǎng)景。比如每 6 小時(shí)執(zhí)行 bgsave 備份,并把 RDB 文件復(fù)制到遠(yuǎn)程機(jī)器或者文件系統(tǒng)中(如 hdfs)用于災(zāi)備。
- Redis 加載 RDB 恢復(fù)數(shù)據(jù)遠(yuǎn)遠(yuǎn)快于 AOF 的方式。 (二進(jìn)制的方式則直接將數(shù)據(jù)讀取到內(nèi)存中,按照字節(jié)的格式取出來(lái)放到結(jié)構(gòu)體 / 對(duì)象即可,文本方式組織數(shù)據(jù)則需要進(jìn)行一系列的字符串切分操作)
(2)缺點(diǎn)
- RDB 方式數(shù)據(jù)沒(méi)辦法做到實(shí)時(shí)持久化 / 秒級(jí)持久化(在兩次生成快照之間,實(shí)時(shí)數(shù)據(jù)可能會(huì)隨著重啟而丟失),這就導(dǎo)致快照里的數(shù)據(jù)和當(dāng)前實(shí)時(shí)的數(shù)據(jù)情況可能存在偏差。因?yàn)?bgsave 每次運(yùn)行都要執(zhí)行 fork 創(chuàng)建子進(jìn)程,屬于重量級(jí)操作,頻繁執(zhí)行成本過(guò)高。
- RDB 文件使用特定二進(jìn)制格式保存,Redis 版本演進(jìn)過(guò)程中有多個(gè) RDB 版本,兼容性可能有風(fēng)險(xiǎn)(舊版本的 Redis 的 rdb 文件放到新版本的 Redis 中不一定能實(shí)現(xiàn)。但一般來(lái)說(shuō),實(shí)際工作中 Redis 版本都是統(tǒng)一的,實(shí)在不行也可以通過(guò)寫(xiě)一個(gè)程序的方式來(lái)直接遍歷舊的 Redis 中的所有 key,把數(shù)據(jù)取出來(lái)插入到新的 Redis 服務(wù)器中即可)。
二、AOF
AOF(Append Only File)持久化(類(lèi)似于 MySQL 中的 binlog,會(huì)把用戶(hù)的每個(gè)操作都記錄到文件中):以獨(dú)立日志的方式記錄每次寫(xiě)命令,重啟時(shí)再重新執(zhí)行 AOF 文件中的命令達(dá)到恢復(fù)數(shù)據(jù)的目的。AOF 的主要作用是解決了數(shù)據(jù)持久化的實(shí)時(shí)性,目前已經(jīng)是 Redis 持久化的主流方式。
Redis 重新啟動(dòng)時(shí),又讀 RDB、又讀 AOF,到底以哪個(gè)為準(zhǔn)呢?
當(dāng)開(kāi)啟 AOF 時(shí),rdb 就不生效了,啟動(dòng)的時(shí)候就不再讀取 rdb 文件內(nèi)容。
1、使用 AOF
開(kāi)啟 AOF 功能需要設(shè)置配置:appendonly yes,默認(rèn)是關(guān)閉狀態(tài)。AOF 文件名通過(guò) appendfilename 配置(默認(rèn)是 appendonly.aof)設(shè)置。


保存目錄同 RDB 持久化方式一致,通過(guò) dir 配置指定(/var/lib/redis)。AOF 的工作流程操作:命令寫(xiě)入(append)、文件同步(sync)、文件重寫(xiě)(rewrite)、重啟加載(load),如下圖所示:
AOF 工作流程:

- 所有的寫(xiě)入命令會(huì)追加到 aof_buf(內(nèi)存中的緩沖區(qū),大大降低了寫(xiě)硬盤(pán)的次數(shù))中。
- AOF 緩沖區(qū)根據(jù)對(duì)應(yīng)的策略向硬盤(pán)做同步操作。
- 隨著 AOF 文件越來(lái)越大,需要定期對(duì) AOF 文件進(jìn)行重寫(xiě),達(dá)到壓縮的目的。
- 當(dāng) Redis 服務(wù)器啟動(dòng)時(shí),可以加載 AOF 文件進(jìn)行數(shù)據(jù)恢復(fù)。
注意:寫(xiě)硬盤(pán)的時(shí)候,寫(xiě)入硬盤(pán)數(shù)據(jù)的多少對(duì)于性能的影響不是很大,但是寫(xiě)入硬盤(pán)的次數(shù)則影響很大了。
硬盤(pán)上讀寫(xiě)數(shù)據(jù),順序讀寫(xiě)的速度還是比較快的(但還是比內(nèi)存要慢很多),隨機(jī)訪問(wèn)則速度比較慢。AOF 是每次將新的數(shù)據(jù)寫(xiě)入到原有文件的末尾,屬于順序?qū)懭搿?/p>
2、命令寫(xiě)入
AOF 命令寫(xiě)入的內(nèi)容直接是文本協(xié)議格式。

每次進(jìn)行的操作都會(huì)被記錄到文本文件中,通過(guò)一些特殊符號(hào)作為分隔符,來(lái)對(duì)命令的細(xì)節(jié)做出區(qū)分。


此處遵守 Redis 格式協(xié)議,Redis 選擇文本協(xié)議可能的原因:
- 文本協(xié)議具備較好的兼容性。
- 實(shí)現(xiàn)簡(jiǎn)單。
- 具備可讀性。
AOF 過(guò)程中為什么需要 aof_buf 這個(gè)緩沖區(qū)?
Redis 雖然是使用單線程響應(yīng)命令,但是速度很快,因?yàn)樗皇遣僮鲀?nèi)存。引入 AOF 之后,又要寫(xiě)內(nèi)存,又要寫(xiě)硬盤(pán),同時(shí)還要保持之前的速度。實(shí)際上,這并沒(méi)有影響到 Redis 處理請(qǐng)求的速度。
AOF 機(jī)制并非是直接讓工作線程把數(shù)據(jù)寫(xiě)入硬盤(pán),而是先寫(xiě)入一個(gè)內(nèi)存中的緩沖區(qū),積累一波之后再統(tǒng)一寫(xiě)入。如果每次寫(xiě) AOF 文件都直接同步硬盤(pán),性能從內(nèi)存的讀寫(xiě)變成 IO 讀寫(xiě),必然會(huì)下降。先寫(xiě)入緩沖區(qū)可以有效減少 IO 次數(shù),同時(shí),Redis 還可以提供多種緩沖區(qū)同步(刷新)策略,讓用戶(hù)根據(jù)自己的實(shí)際情況和需求來(lái)做出合理的平衡。
如果把數(shù)據(jù)寫(xiě)入到緩沖區(qū)中,其本質(zhì)還是在內(nèi)存中。萬(wàn)一此時(shí)突然進(jìn)程掛了或者主機(jī)掉點(diǎn)了,那緩沖區(qū)中的數(shù)據(jù)是否就丟了呢?
是的。緩沖區(qū)中沒(méi)來(lái)得及寫(xiě)入硬盤(pán)的數(shù)據(jù)是會(huì)丟失的。
- 緩沖區(qū)刷新頻率越高,性能影響就越大,同時(shí)數(shù)據(jù)的可靠性就越高。
- 緩沖區(qū)刷新頻率越低,性能影響就越小,同時(shí)數(shù)據(jù)的可靠性就越低。
3、文件同步
Redis 提供了多種 AOF 緩沖區(qū)同步文件策略,由參數(shù) appendfsync 控制,不同值的含義如下表所示。
AOF 緩沖區(qū)同步文件策略:

默認(rèn)采用 everysec 配置:

系統(tǒng)調(diào)用 write 和 fsync 說(shuō)明:
- write 操作會(huì)觸發(fā)延遲寫(xiě)(delayed write)機(jī)制。Linux 在內(nèi)核提供頁(yè)緩沖區(qū)用來(lái)提供硬盤(pán) IO 性能。write 操作在寫(xiě)入系統(tǒng)緩沖區(qū)后立即返回。同步硬盤(pán)操作依賴(lài)于系統(tǒng)調(diào)度機(jī)制,例如:緩沖區(qū)頁(yè)空間寫(xiě)滿(mǎn)或達(dá)到特定時(shí)間周期。同步文件之前,如果此時(shí)系統(tǒng)故障宕機(jī),緩沖區(qū)內(nèi)數(shù)據(jù)將丟失。
- Fsync 針對(duì)單個(gè)文件操作,做強(qiáng)制硬盤(pán)同步,fsync 將阻塞直到數(shù)據(jù)寫(xiě)入到硬盤(pán)。
- 配置為 always 時(shí),每次寫(xiě)入都要同步 AOF 文件,性能很差,在?般的 SATA 硬盤(pán)上,只能?持大約幾百 TPS 寫(xiě)入。除非是非常重要的數(shù)據(jù),否則不建議配置。
- 配置為 no 時(shí),由于操作系統(tǒng)同步策略不可控,雖然提高了性能,但數(shù)據(jù)丟失風(fēng)險(xiǎn)大增,除非數(shù)據(jù)重要程度很低,一般不建議配置。
- 配置為 everysec,是默認(rèn)配置,也是推薦配置,兼顧了數(shù)據(jù)安全性和性能。理論上最多丟失 1s 的數(shù)據(jù)。
4、重寫(xiě)機(jī)制
隨著命令不斷寫(xiě)入 AOF,文件持續(xù)增長(zhǎng),體積會(huì)越來(lái)越大,會(huì)影響到 Redis 下次啟動(dòng)的啟動(dòng)時(shí)間(Redis 在啟動(dòng)時(shí)需要讀取 AOF 文件內(nèi)容,該文件記錄了中間的過(guò)程,但實(shí)際上 Redis 在重啟時(shí)只關(guān)注最終結(jié)果)。
為了解決這個(gè)問(wèn)題,Redis 引入 AOF 重寫(xiě)機(jī)制(能夠針對(duì) AOF 文件進(jìn)行整理操作,剔除其中的冗余操作并合并一些操作,以此達(dá)到給文件 “瘦身” 的效果)壓縮文件體積。
AOF 文件重寫(xiě)是把 Redis 進(jìn)程內(nèi)的數(shù)據(jù)轉(zhuǎn)化為寫(xiě)命令同步到新的 AOF 文件。

重寫(xiě)后的 AOF 為什么可以變???
- 進(jìn)程內(nèi)已超時(shí)的數(shù)據(jù)不再寫(xiě)入文件。
- 舊的 AOF 中的無(wú)效命令,例如 del、hdel、srem 等重寫(xiě)后將會(huì)刪除,只需要保留數(shù)據(jù)的最終版本。
- 多條寫(xiě)操作合并為?條,例如 lpush list a、lpush list b、lpush list 從可以合并為 lpush list a b c。
較小的 AOF 文件一方面降低了硬盤(pán)空間占用,一方面可以提升啟動(dòng) Redis 時(shí)數(shù)據(jù)恢復(fù)的速度。
AOF 重寫(xiě)過(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:表示觸發(fā)重寫(xiě)時(shí) AOF 的最小文件大小,默認(rèn)為 64MB。
- auto-aof-rewrite-percentage:代表當(dāng)前 AOF 占用大小相比較上次重寫(xiě)時(shí)增加的比例。
當(dāng)觸發(fā) AOF 重寫(xiě)時(shí),下圖介紹它的運(yùn)行流程。
AOF 重寫(xiě)流程:

- 執(zhí)行 AOF 重寫(xiě)請(qǐng)求。如果當(dāng)前進(jìn)程正在執(zhí)行 AOF 重寫(xiě),請(qǐng)求不執(zhí)行。如果當(dāng)前進(jìn)程正在執(zhí)行 bgsave 操作,重寫(xiě)命令延遲到 bgsave 完成之后再執(zhí)行。
- 父進(jìn)程執(zhí)行 fork 創(chuàng)建子進(jìn)程。
- 重寫(xiě)。a. 父進(jìn)程 fork 之后,繼續(xù)響應(yīng)其他命令,仍然負(fù)責(zé)接收客戶(hù)端的新的請(qǐng)求。父進(jìn)程還是會(huì)把這些請(qǐng)求產(chǎn)生的 AOF 數(shù)據(jù)先寫(xiě)入到緩沖區(qū)中并根據(jù) appendfsync 策略同步到硬盤(pán),保證舊 AOF 文件機(jī)制正確。b. 子進(jìn)程只有 fork 之前的所有內(nèi)存信息,父進(jìn)程中需要將 fork 之后這段時(shí)間的修改操作寫(xiě)入 AOF 重寫(xiě)緩沖區(qū)中。重寫(xiě)的時(shí)候不關(guān)心 AOF 文件中原來(lái)有什么,只關(guān)心內(nèi)存中最終的數(shù)據(jù)狀態(tài)。(內(nèi)存中的數(shù)據(jù)的狀態(tài),就已經(jīng)相當(dāng)于是把 AOF 文件結(jié)果整理后的模樣了)
- 在創(chuàng)建子進(jìn)程的一瞬間,子進(jìn)程就根據(jù)內(nèi)存快照,將命令合并到新的 AOF 文件中,也就是繼承了當(dāng)前父進(jìn)程的內(nèi)存狀態(tài)。因此,子進(jìn)程里的內(nèi)存數(shù)據(jù)是父進(jìn)程 fork 之前的狀態(tài)。fork 后新來(lái)的請(qǐng)求對(duì)內(nèi)存造成的修改是子進(jìn)程感知不到的。此時(shí),父進(jìn)程這里又準(zhǔn)備了一個(gè) aof_rewrite_buf 緩沖區(qū)(專(zhuān)門(mén)存放 fork 后收到的數(shù)據(jù))。
- 子進(jìn)程完成重寫(xiě):
子進(jìn)程這邊把 AOF 新文件數(shù)據(jù)寫(xiě)入完成后,子進(jìn)程會(huì)通過(guò)發(fā)送信號(hào)通知父進(jìn)程。
父進(jìn)程再把 aof_rewrite_buf 緩沖區(qū)中的內(nèi)容也追加到新 AOF 文件里。
用新 AOF 文件代替舊 AOF 文件。
此處子進(jìn)程寫(xiě)數(shù)據(jù)的過(guò)程非常類(lèi)似于 RDB 生成一個(gè)鏡像快照,只不過(guò) RDB 是按照二進(jìn)制的方式來(lái)生成的,AOF 重寫(xiě)則是按照 AOF 這里要求的文本格式來(lái)生成的。二者目的都是為了把當(dāng)前內(nèi)存中的所有數(shù)據(jù)狀態(tài)記錄到文件中。
如果在執(zhí)行 bgrewriteaof 的時(shí)候,發(fā)現(xiàn)當(dāng)前 Redis 已經(jīng)正在進(jìn)行 AOF 重寫(xiě)了,會(huì)怎樣呢?
此時(shí)不會(huì)再次執(zhí)行 AOF 重寫(xiě),而是直接返回了。
如果在執(zhí)行 bgrewriteaof 的時(shí)候,發(fā)現(xiàn)當(dāng)前 Redis 在生成 rdb 文件的快照,會(huì)怎樣呢?
此時(shí) AOF 重寫(xiě)操作就會(huì)等待 rdb 快照生成完畢之后再進(jìn)行執(zhí)行 AOF 重寫(xiě)。
為什么 RDB 對(duì)于 fork 之后的新數(shù)據(jù)就直接置之不理了呢,而不選擇采用和 AOF 一樣的處理機(jī)制呢?
RDB 本身的設(shè)計(jì)理念就是用來(lái) “定期備份” 的。只要是定期備份就難以和最新數(shù)據(jù)保持一致。
實(shí)時(shí)備份不一定就比定期備份更好,具體還是要看實(shí)際場(chǎng)景需求?,F(xiàn)在的系統(tǒng)中,系統(tǒng)資源一般都是比較充裕的,AOF 的開(kāi)銷(xiāo)并不大,所以一般來(lái)說(shuō),AOF 的適用場(chǎng)景更多一些。
父進(jìn)程 fork 完畢之后,就已經(jīng)讓子進(jìn)程寫(xiě)新的 AOF 文件了。并且隨著時(shí)間的推移,子進(jìn)程很快就寫(xiě)完了新的文件,要讓新的 AOF 文件代替舊的 AOF 文件。父進(jìn)程此時(shí)還在繼續(xù)寫(xiě)這個(gè)即將消亡的舊的 AOF 文件是否還有意義呢?
需要考慮到極端情況:假設(shè)在重寫(xiě)的過(guò)程中,重寫(xiě)了一半服務(wù)器突然掛了,此時(shí)子進(jìn)程內(nèi)存的數(shù)據(jù)就會(huì)丟失,新的 AOF 文件內(nèi)容還不完整,所以如果父進(jìn)程不堅(jiān)持寫(xiě)舊的 AOF 文件,那么重啟就無(wú)法保證數(shù)據(jù)的完整性了。




AOF 本來(lái)是按照文本的方式來(lái)寫(xiě)入文件的,但是文本的方式來(lái)寫(xiě)文件,后續(xù)加載的成本較高。所以,Redis 就引入了 “混合持久化” 的方式,結(jié)合了 rdb 和 aof 的特點(diǎn)。
按照 aof 的方式,將每一個(gè)請(qǐng)求 / 操作都記錄入文件中。在觸發(fā) aof 重寫(xiě)之后,就會(huì)把當(dāng)前內(nèi)存的狀態(tài)按照 rdb 的二進(jìn)制格式寫(xiě)入到新的 aof 文件中。

后續(xù)再進(jìn)行的操作仍然是按照 aof 文本的方式追加到文件后面:


5、啟動(dòng)時(shí)數(shù)據(jù)恢復(fù)
當(dāng) Redis 啟動(dòng)時(shí),會(huì)根據(jù) RDB 和 AOF 文件的內(nèi)容,進(jìn)行數(shù)據(jù)恢復(fù),如下圖所示。
Redis 根據(jù)持久化文件進(jìn)行數(shù)據(jù)恢復(fù):

三、RDB 和 AOF 的區(qū)別和聯(lián)系
- RDB 和 AOF 是 Redis 提供了兩種持久化方案。
- RDB 視為內(nèi)存的快照,產(chǎn)生的內(nèi)容更為緊湊,占用空間較小,恢復(fù)時(shí)速度更快。但產(chǎn)? RDB 的開(kāi)銷(xiāo)較大,不適合進(jìn)行實(shí)時(shí)持久化,一般用于冷備和主從復(fù)制。
- AOF 視為對(duì)修改命令保存,在恢復(fù)時(shí)需要重放命令,并且有重寫(xiě)機(jī)制來(lái)定期壓縮 AOF 文件。
- RDB 和 AOF 都使用 fork 創(chuàng)建子進(jìn)程,利用 Linux 子進(jìn)程擁有父進(jìn)程內(nèi)存快照的特點(diǎn)進(jìn)行持久化,盡可能不影響主進(jìn)程繼續(xù)處理后續(xù)命令。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Redis設(shè)置鍵的生存時(shí)間或過(guò)期時(shí)間的方法詳解
這篇文章主要介紹了Redis如何設(shè)置鍵的生存時(shí)間或過(guò)期時(shí)間,通過(guò)EXPIRE命令或者PEXIPIRE命令,客戶(hù)端可以以秒或者毫秒精度為數(shù)據(jù)庫(kù)中的某個(gè)鍵設(shè)置生存時(shí)間,文中有詳細(xì)的代碼供供大家參考,需要的朋友可以參考下2024-03-03
Redis實(shí)現(xiàn)限流器的三種方法(小結(jié))
本文主要介紹了Redis實(shí)現(xiàn)限流器的三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
基于Redisson實(shí)現(xiàn)分布式系統(tǒng)下的接口限流
在高并發(fā)場(chǎng)景下,接口限流是保障系統(tǒng)穩(wěn)定性的重要手段,本文將介紹利用Redisson結(jié)合Redis實(shí)現(xiàn)分布式環(huán)境下的接口限流,具有一定的參考價(jià)值,感興趣的可以了解一下2025-08-08
淺析Redis Sentinel 與 Redis Cluster
本文主要介紹Redis Sentinel 及 Redis Cluster的區(qū)別及用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06

