源碼級別理解 Redis 持久化機(jī)制

前言

大家都知道 Redis 是一個(gè)內(nèi)存數(shù)據(jù)庫,數(shù)據(jù)都存儲在內(nèi)存中,這也是 Redis 非常快的原因之一。雖然速度提上來了,但是如果數(shù)據(jù)一直放在內(nèi)存中,是非常容易丟失的。比如 服務(wù)器關(guān)閉或宕機(jī)了,內(nèi)存中的數(shù)據(jù)就木有了。為了解決這一問題,Redis 提供了 持久化 機(jī)制。分別是 RDB 以及 AOF 持久化。

RDB

什么是 RDB 持久化?

RDB 持久化可以在指定的時(shí)間間隔內(nèi)生成數(shù)據(jù)集的時(shí)間點(diǎn)快照(point-in-time snapshot)。

源碼級別理解 Redis 持久化機(jī)制

RDB 的優(yōu)點(diǎn)?

  • RDB 是一種表示某個(gè)即時(shí)點(diǎn)的 Redis 數(shù)據(jù)的緊湊文件。RDB 文件適用于備份。例如,你可能想要每小時(shí)歸檔最近24小時(shí)的 RDB 文件,每天保存近30天的 RDB 快照。這允許你很容易的恢復(fù)不同版本的數(shù)據(jù)集以容災(zāi)。
  • RDB 非常適合于災(zāi)難恢復(fù),作為一個(gè)緊湊的單一文件,可以被傳輸?shù)竭h(yuǎn)程的數(shù)據(jù)中心。
  • RDB 最大化了 Redis 的性能。因?yàn)?Redis 父進(jìn)程持久化時(shí)唯一需要做的是啟動(fork)一個(gè)子進(jìn)程,由子進(jìn)程完成所有剩余的工作。父進(jìn)程實(shí)例不需要執(zhí)行像磁盤IO這樣的操作。
  • RDB 在重啟保存了大數(shù)據(jù)集的實(shí)例比 AOF 快。

RDB 的缺點(diǎn)?

  • 當(dāng)你需要在Redis停止工作(例如停電)時(shí)最小化數(shù)據(jù)丟失,RDB可能不太好。你可以配置不同的保存點(diǎn)(save point)來保存RDB文件(例如,至少5分鐘和對數(shù)據(jù)集100次寫之后,但是你可以有多個(gè)保存點(diǎn))。然而,你通常每隔5分鐘或更久創(chuàng)建一個(gè)RDB快照,所以一旦Redis因?yàn)槿魏卧驔]有正確關(guān)閉而停止工作,你就得做好最近幾分鐘數(shù)據(jù)丟失的準(zhǔn)備了。
  • RDB需要經(jīng)常調(diào)用fork()子進(jìn)程來持久化到磁盤。如果數(shù)據(jù)集很大的話,fork()比較耗時(shí),結(jié)果就是,當(dāng)數(shù)據(jù)集非常大并且CPU性能不夠強(qiáng)大的話,Redis會停止服務(wù)客戶端幾毫秒甚至一秒。AOF也需要fork(),但是你可以調(diào)整多久頻率重寫日志而不會有損(trade-off)持久性(durability)。

RDB 文件的創(chuàng)建與載入

有個(gè)兩個(gè) Redis 命令可以用于生成 RDB 文件,一個(gè)是 SAVE,另一個(gè)是 BGSAVE。 SAVE 命令會阻塞 Redis 服務(wù)器進(jìn)程,直到 RDB 文件創(chuàng)建完畢為止,在服務(wù)器進(jìn)程阻塞期間,服務(wù)器不能處理任何命令請求。

> SAVE     // 一直等到 RDB 文件創(chuàng)建完畢
OK

和 SAVE 命令直接阻塞服務(wù)器進(jìn)程不同的是,BGSAVE 命令會派生出一個(gè)子進(jìn)程,然后由子進(jìn)程負(fù)責(zé)創(chuàng)建 RDB 文件,服務(wù)器進(jìn)程(父進(jìn)程)繼續(xù)處理命令進(jìn)程。 執(zhí)行fork的時(shí)候操作系統(tǒng)(類Unix操作系統(tǒng))會使用寫時(shí)復(fù)制(copy-on-write)策略,即fork函數(shù)發(fā)生的一刻父子進(jìn)程共享同一內(nèi)存數(shù)據(jù),當(dāng)父進(jìn)程要更改其中某片數(shù)據(jù)時(shí)(如執(zhí)行一個(gè)寫命令 ),操作系統(tǒng)會將該片數(shù)據(jù)復(fù)制一份以保證子進(jìn)程的數(shù)據(jù)不受影響,所以新的RDB文件存儲的是執(zhí)行fork一刻的內(nèi)存數(shù)據(jù)。

> BGSAVE  // 派生子進(jìn)程,并由子進(jìn)程創(chuàng)建 RDB 文件
Background saving started

生成 RDB 文件由兩種方式:一種是手動,就是上邊介紹的用命令的方式;另一種是自動的方式。 接下來詳細(xì)介紹一下自動生成 RDB 文件的流程。
Redis 允許用戶通過設(shè)置服務(wù)器配置的save 選項(xiàng),讓服務(wù)器每隔一段時(shí)間自動執(zhí)行一次 BGSAVE 命令。
用戶可以通過在 redis.conf 配置文件中的 SNAPSHOTTING 下 save 選項(xiàng)設(shè)置多個(gè)保存條件,但只要其中任意一個(gè)條件被滿足,服務(wù)器就會執(zhí)行 BGSAEVE 命令。
如,以下配置:
save 900 1
save 300 10
save 60 10000
上邊三個(gè)配置的含義是:

  • 服務(wù)器在 900 秒內(nèi),對數(shù)據(jù)庫進(jìn)行了至少 1 次修改。
  • 服務(wù)器在 300 秒內(nèi),對數(shù)據(jù)庫進(jìn)行了至少 10 次修改。
  • 服務(wù)器在 60 秒內(nèi),對數(shù)據(jù)庫進(jìn)行了至少 10000 次修改。

如果沒有手動去配置 save 選項(xiàng),那么服務(wù)器會為 save 選項(xiàng)配置默認(rèn)參數(shù):
save 900 1
save 300 10
save 60 10000
接著,服務(wù)器就會根據(jù) save 選項(xiàng)的配置,去設(shè)置服務(wù)器狀態(tài) redisServer 結(jié)構(gòu)的 saveparams 屬性:

struct redisServer{

  // ...

  // 記錄了保存條件的數(shù)組
  struct saveparams *saveparams;

  // ...
};

saveparams 屬性是一個(gè)數(shù)組,數(shù)組中的每一個(gè)元素都是一個(gè) saveparam 結(jié)構(gòu),每個(gè) saveparam 結(jié)構(gòu)都保存了一個(gè) save 選項(xiàng)設(shè)置的保存條件:

struct saveparam {

  // 秒數(shù)
  time_t seconds;

  // 修改數(shù)
  int changes;
};

除了 saveparams 數(shù)組之外,服務(wù)器狀態(tài)還維持著一個(gè) dirty 計(jì)數(shù)器,以及一個(gè) lastsave 屬性;

struct redisServer {
    // ...

    // 修改計(jì)數(shù)器
    long long dirty;

    // 上一次執(zhí)行保存時(shí)間
     time_t lastsave;

     // ...
}

  • dirty 計(jì)數(shù)器記錄距離上一次成功執(zhí)行 SAVE 或 BGSAVE 命令之后,服務(wù)器對數(shù)據(jù)庫狀態(tài)(服務(wù)器中的所有數(shù)據(jù)庫)進(jìn)行了多少次修改(包括寫入、刪除、更新等操作)。
  • lastsave 屬性是一個(gè) UNIX 時(shí)間戳,記錄了服務(wù)器上一次執(zhí)行 SAVE 或 BGSAVE 命令的時(shí)間。

檢查條件是否滿足觸發(fā) RDB

Redis 的服務(wù)器周期性操作函數(shù) serverCron 默認(rèn)每隔 100 毫秒執(zhí)行一次,該函數(shù)用于對正在運(yùn)行的服務(wù)器進(jìn)行維護(hù),它的其中一項(xiàng)工作就是檢查 save 選項(xiàng)所設(shè)置的保存條件是否已經(jīng)滿足,如果滿足的話就執(zhí)行 BGSAVE 命令。
Redis serverCron 源碼解析如下:

源碼級別理解 Redis 持久化機(jī)制

程序會遍歷并檢查 saveparams 數(shù)組中的所有保存條件,只要有任意一個(gè)條件被滿足,服務(wù)器就會執(zhí)行 BGSAVE 命令。
下面是 rdbSaveBackground 的源碼流程:

源碼級別理解 Redis 持久化機(jī)制

RDB 文件結(jié)構(gòu)

下圖展示了一個(gè)完整 RDB 文件所包含的各個(gè)部分。

redis 文件的最開頭是 REDIS 部分,這個(gè)部分的長度是 5 字節(jié),保存著 “REDIS” 五個(gè)字符。通過這五個(gè)字符,程序可以在載入文件時(shí),快速檢查所載入的文件是否時(shí) RDB 文件。

db_version 長度為 4 字節(jié),他的值時(shí)一個(gè)字符串表示的整數(shù),這個(gè)整數(shù)記錄了 RDB 文件的版本號,比如 “0006” 就代表 RDB 文件的版本為第六版。

database 部分包含著零個(gè)或任意多個(gè)數(shù)據(jù)庫,以及各個(gè)數(shù)據(jù)庫中的鍵值對數(shù)據(jù):

  • 如果服務(wù)器的數(shù)據(jù)庫狀態(tài)為空(所有數(shù)據(jù)庫都是空的),那么這個(gè)部分也為空,長度為 0 字節(jié)。
  • 如果服務(wù)器的數(shù)據(jù)庫狀態(tài)為非空(有至少一個(gè)數(shù)據(jù)庫非空),那么這個(gè)部分也為非空,根據(jù)數(shù)據(jù)庫所保存鍵值對的數(shù)量、類型和內(nèi)容不同,這個(gè)部分的長度也會有所不同。

EOF 常量的長度為 1 字節(jié),這個(gè)常量標(biāo)志著 RDB 文件正文內(nèi)容的結(jié)束,當(dāng)讀入程序遇到這個(gè)值后,他知道所有數(shù)據(jù)庫的所有鍵值對已經(jīng)載入完畢了。

check_sum 是一個(gè) 8 字節(jié)長的無符號整數(shù),保存著一個(gè)校驗(yàn)和,這個(gè)校驗(yàn)和時(shí)程序通過對 REDIS、db_version、database、EOF 四個(gè)部分的內(nèi)容進(jìn)行計(jì)算得出的。服務(wù)器在載入 RDB 文件時(shí),會將載入數(shù)據(jù)所計(jì)算出的校驗(yàn)和與 check_sum 所記錄的校驗(yàn)和進(jìn)行對比,以此來檢查 RDB 是否有出錯(cuò)或者損壞的情況。
舉個(gè)例子:下圖是一個(gè) 0 號數(shù)據(jù)庫和 3 號數(shù)據(jù)庫的 RDB 文件。第一個(gè)就是 “REDIS” 表示是一個(gè) RDB 文件,之后的 “0006” 表示這是第六版的 REDIS 文件,然后是兩個(gè)數(shù)據(jù)庫,之后就是 EOF 結(jié)束標(biāo)識符,最后就是 check_sum。

AOF 持久化

什么是 AOF 持久化

AOF持久化方式記錄每次對服務(wù)器寫的操作,當(dāng)服務(wù)器重啟的時(shí)候會重新執(zhí)行這些命令來恢復(fù)原始的數(shù)據(jù),AOF命令以redis協(xié)議追加保存每次寫的操作到文件末尾.Redis還能對AOF文件進(jìn)行后臺重寫,使得AOF文件的體積不至于過大.

AOF 的優(yōu)點(diǎn)?

  • 使用AOF 會讓你的Redis更加耐久: 你可以使用不同的fsync策略:無fsync,每秒fsync,每次寫的時(shí)候fsync.使用默認(rèn)的每秒fsync策略,Redis的性能依然很好(fsync是由后臺線程進(jìn)行處理的,主線程會盡力處理客戶端請求),一旦出現(xiàn)故障,你最多丟失1秒的數(shù)據(jù).
  • AOF文件是一個(gè)只進(jìn)行追加的日志文件,所以不需要寫入seek,即使由于某些原因(磁盤空間已滿,寫的過程中宕機(jī)等等)未執(zhí)行完整的寫入命令,你也也可使用redis-check-aof工具修復(fù)這些問題.
  • Redis 可以在 AOF 文件體積變得過大時(shí),自動地在后臺對 AOF 進(jìn)行重寫: 重寫后的新 AOF 文件包含了恢復(fù)當(dāng)前數(shù)據(jù)集所需的最小命令集合。 整個(gè)重寫操作是絕對安全的,因?yàn)?Redis 在創(chuàng)建新 AOF 文件的過程中,會繼續(xù)將命令追加到現(xiàn)有的 AOF 文件里面,即使重寫過程中發(fā)生停機(jī),現(xiàn)有的 AOF 文件也不會丟失。 而一旦新 AOF 文件創(chuàng)建完畢,Redis 就會從舊 AOF 文件切換到新 AOF 文件,并開始對新 AOF 文件進(jìn)行追加操作。
  • AOF 文件有序地保存了對數(shù)據(jù)庫執(zhí)行的所有寫入操作, 這些寫入操作以 Redis 協(xié)議的格式保存, 因此 AOF 文件的內(nèi)容非常容易被人讀懂, 對文件進(jìn)行分析(parse)也很輕松。 導(dǎo)出(export) AOF 文件也非常簡單: 舉個(gè)例子, 如果你不小心執(zhí)行了 FLUSHALL 命令, 但只要 AOF 文件未被重寫, 那么只要停止服務(wù)器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重啟 Redis , 就可以將數(shù)據(jù)集恢復(fù)到 FLUSHALL 執(zhí)行之前的狀態(tài)。

AOF 的缺點(diǎn)?

  • 對于相同的數(shù)據(jù)集來說,AOF 文件的體積通常要大于 RDB 文件的體積。
  • 根據(jù)所使用的 fsync 策略,AOF 的速度可能會慢于 RDB 。 在一般情況下, 每秒 fsync 的性能依然非常高, 而關(guān)閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負(fù)荷之下也是如此。 不過在處理巨大的寫入載入時(shí),RDB 可以提供更有保證的最大延遲時(shí)間(latency)。

AOF持久化的實(shí)現(xiàn)

AOF持久化功能的實(shí)現(xiàn)可以分為命令追加(append)、文件寫入、文件同步(sync)三個(gè)步驟。

命令追加

當(dāng) AOF 持久化功能處于打開狀態(tài)時(shí),服務(wù)器在執(zhí)行完一個(gè)寫命令之后,會以協(xié)議格式將被執(zhí)行的寫命令追加到服務(wù)器狀態(tài)的 aof_buf 緩沖區(qū)的末尾。

struct redisServer {
  // ...
  // AOF 緩沖區(qū)  
  sds aof_buf;

  // ..
};

如果客戶端向服務(wù)器發(fā)送以下命令:

> set KEY VALUE
OK

那么服務(wù)器在執(zhí)行這個(gè) set 命令之后,會將以下協(xié)議內(nèi)容追加到 aof_buf 緩沖區(qū)的末尾;

*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n

AOF 文件的寫入與同步

Redis的服務(wù)器進(jìn)程就是一個(gè)事件循環(huán)(loop),這個(gè)循環(huán)中的文件事件負(fù)責(zé)接收客戶端
的命令請求,以及向客戶端發(fā)送命令回復(fù),而時(shí)間事件則負(fù)責(zé)執(zhí)行像 serverCron 函數(shù)這樣需
要定時(shí)運(yùn)行的函數(shù)。
因?yàn)榉?wù)器在處理文件事件時(shí)可能會執(zhí)行寫命令,使得一些內(nèi)容被追加到aof_buf緩沖區(qū)
里面,所以在服務(wù)器每次結(jié)束一個(gè)事件循環(huán)之前,它都會調(diào)用 flushAppendOnlyFile 函數(shù),考
慮是否需要將aof_buf緩沖區(qū)中的內(nèi)容寫入和保存到AOF文件里面,這個(gè)過程可以用以下偽代
碼表示:


flushAppendOnlyFile函數(shù)的行為由服務(wù)器配置的 appendfsync 選項(xiàng)的值來決定,各個(gè)不同
值產(chǎn)生的行為如下表所示。

如果用戶沒有主動為appendfsync選項(xiàng)設(shè)置值,那么appendfsync選項(xiàng)的默認(rèn)值為everysec。
寫到這里有的小伙伴可能會對上面說的寫入和同步含義弄混,這里說一下: 寫入:將 aof_buf 中的數(shù)據(jù)寫入到 AOF 文件中。 同步:調(diào)用 fsync 以及 fdatasync 函數(shù),將 AOF 文件中的數(shù)據(jù)保存到磁盤中。 通俗地講就是,你要往一個(gè)文件寫東西,寫的過程就是寫入,而同步則是將文件保存,數(shù)據(jù)落到磁盤上。
大家之前看文章的時(shí)候是不是大多都說 AOF 最多丟失一秒鐘的數(shù)據(jù),那是因?yàn)?redis AOF 默認(rèn)是 everysec 策略,這個(gè)策略每秒執(zhí)行一次,所以 AOF 持久化最多丟失一秒鐘的數(shù)據(jù)。

AOF 文件的載入與數(shù)據(jù)還原

因?yàn)锳OF文件里面包含了重建數(shù)據(jù)庫狀態(tài)所需的所有寫命令,所以服務(wù)器只要讀入并重新執(zhí)行一遍AOF文件里面保存的寫命令,就可以還原服務(wù)器關(guān)閉之前的數(shù)據(jù)庫狀態(tài)。 Redis讀取AOF文件并還原數(shù)據(jù)庫狀態(tài)的詳細(xì)步驟如下:

  1. 創(chuàng)建一個(gè)不帶網(wǎng)絡(luò)連接的偽客戶端(fake client):因?yàn)镽edis的命令只能在客戶端上 下文中執(zhí)行,而載入AOF文件時(shí)所使用的命令直接來源于AOF文件而不是網(wǎng)絡(luò)連接,所以服 務(wù)器使用了一個(gè)沒有網(wǎng)絡(luò)連接的偽客戶端來執(zhí)行AOF文件保存的寫命令,偽客戶端執(zhí)行命令 的效果和帶網(wǎng)絡(luò)連接的客戶端執(zhí)行命令的效果完全一樣。
  2. 從AOF文件中分析并讀取出一條寫命令。
  3. 使用偽客戶端執(zhí)行被讀出的寫命令。
  4. 一直執(zhí)行步驟2和步驟3,直到AOF文件中的所有寫命令都被處理完畢為止。
    當(dāng)完成以上步驟之后,AOF文件所保存的數(shù)據(jù)庫狀態(tài)就會被完整地還原出來,整個(gè)過程 如下圖所示。

AOF 重寫

因?yàn)锳OF持久化是通過保存被執(zhí)行的寫命令來記錄數(shù)據(jù)庫狀態(tài)的,所以隨著服務(wù)器運(yùn)行 時(shí)間的流逝,AOF文件中的內(nèi)容會越來越多,文件的體積也會越來越大,如果不加以控制的 話,體積過大的AOF文件很可能對Redis服務(wù)器、甚至整個(gè)宿主計(jì)算機(jī)造成影響,并且AOF文 件的體積越大,使用AOF文件來進(jìn)行數(shù)據(jù)還原所需的時(shí)間就越多。
如 客戶端執(zhí)行了以下命令是:

> rpush list "A" "B"
OK
> rpush list "C"
OK
> rpush list "D"
OK
> rpush list "E" "F"
OK

那么光是為了記錄這個(gè)list鍵的狀態(tài),AOF文件就需要保存四條命令。
對于實(shí)際的應(yīng)用程度來說,寫命令執(zhí)行的次數(shù)和頻率會比上面的簡單示例要高得多,所 以造成的問題也會嚴(yán)重得多。 為了解決AOF文件體積膨脹的問題,Redis提供了AOF文件重寫(rewrite)功能。通過該 功能,Redis服務(wù)器可以創(chuàng)建一個(gè)新的AOF文件來替代現(xiàn)有的AOF文件,新舊兩個(gè)AOF文件所 保存的數(shù)據(jù)庫狀態(tài)相同,但新AOF文件不會包含任何浪費(fèi)空間的冗余命令,所以新AOF文件 的體積通常會比舊AOF文件的體積要小得多。 在接下來的內(nèi)容中,我們將介紹AOF文件重寫的實(shí)現(xiàn)原理,以及BGREWEITEAOF命令 的實(shí)現(xiàn)原理。
雖然Redis將生成新AOF文件替換舊AOF文件的功能命名為“AOF文件重寫”,但實(shí)際上, AOF文件重寫并不需要對現(xiàn)有的AOF文件進(jìn)行任何讀取、分析或者寫入操作,這個(gè)功能是通 過讀取服務(wù)器當(dāng)前的數(shù)據(jù)庫狀態(tài)來實(shí)現(xiàn)的。
就像上面的情況,服務(wù)器完全可以將這六條命令合并成一條。

> rpush list "A" "B" "C" "D" "E" "F"

除了上面列舉的列表鍵之外,其他所有類型的鍵都可以用同樣的方法去減少 AOF文件中的命令數(shù)量。首先從數(shù)據(jù)庫中讀取鍵現(xiàn)在的值,然后用一條命令去記錄鍵值對,代替之前記錄這個(gè)鍵值對的多條命令,這就是AOF重寫功能的實(shí)現(xiàn)原理。
在實(shí)際中,為了避免在執(zhí)行命令時(shí)造成客戶端輸入緩沖區(qū)溢出,重寫程序在處理列表、 哈希表、集合、有序集合這四種可能會帶有多個(gè)元素的鍵時(shí),會先檢查鍵所包含的元素?cái)?shù) 量,如果元素的數(shù)量超過了
redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值,那 么重寫程序?qū)⑹褂枚鄺l命令來記錄鍵的值,而不單單使用一條命令。 在目前版本中,REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值為64,這也就是 說,如果一個(gè)集合鍵包含了超過64個(gè)元素,那么重寫程序會用多條SADD命令來記錄這個(gè)集 合,并且每條命令設(shè)置的元素?cái)?shù)量也為64個(gè)。

AOF 后臺重寫

AOF 重寫會執(zhí)行大量的寫操作,這樣會影響主線程,所以redis AOF 重寫放到了子進(jìn)程去執(zhí)行。這樣可以達(dá)到兩個(gè)目的:

  • 子進(jìn)程進(jìn)行AOF重寫期間,服務(wù)器進(jìn)程(父進(jìn)程)可以繼續(xù)處理命令請求。
  • 子進(jìn)程帶有服務(wù)器進(jìn)程的數(shù)據(jù)副本,使用子進(jìn)程而不是線程,可以在避免使用鎖的情況 下,保證數(shù)據(jù)的安全性。

但是有一個(gè)問題,當(dāng)子進(jìn)程重寫數(shù)據(jù)時(shí),主進(jìn)程依然在處理新的數(shù)據(jù),這也就會造成數(shù)據(jù)不一致情況。 為了解決這種數(shù)據(jù)不一致問題,Redis服務(wù)器設(shè)置了一個(gè)AOF重寫緩沖區(qū),這個(gè)緩沖區(qū)在 服務(wù)器創(chuàng)建子進(jìn)程之后開始使用,當(dāng)Redis服務(wù)器執(zhí)行完一個(gè)寫命令之后,它會同時(shí)將這個(gè)寫 命令發(fā)送給AOF緩沖區(qū)和AOF重寫緩沖區(qū),如下圖:

這也就是說,在子進(jìn)程執(zhí)行AOF重寫期間,服務(wù)器進(jìn)程需要執(zhí)行以下三個(gè)工作:

  1. 執(zhí)行客戶端發(fā)來的命令。
  2. 將執(zhí)行后的寫命令追加到AOF緩沖區(qū)。
  3. 將執(zhí)行后的寫命令追加到AOF重寫緩沖區(qū)。

這樣一來可以保證:

  • AOF緩沖區(qū)的內(nèi)容會定期被寫入和同步到AOF文件,對現(xiàn)有AOF文件的處理工作會如常 進(jìn)行。
  • 從創(chuàng)建子進(jìn)程開始,服務(wù)器執(zhí)行的所有寫命令都會被記錄到AOF重寫緩沖區(qū)里面。

當(dāng)子進(jìn)程完成AOF重寫工作之后,它會向父進(jìn)程發(fā)送一個(gè)信號,父進(jìn)程在接到該信號之 后,會調(diào)用一個(gè)信號處理函數(shù),并執(zhí)行以下工作:

  1. 將AOF重寫緩沖區(qū)中的所有內(nèi)容寫入到新AOF文件中,這時(shí)新AOF文件所保存的數(shù) 據(jù)庫狀態(tài)將和服務(wù)器當(dāng)前的數(shù)據(jù)庫狀態(tài)一致。
  2. 對新的AOF文件進(jìn)行改名,原子地(atomic)覆蓋現(xiàn)有的AOF文件,完成新舊兩個(gè) AOF文件的替換。

這個(gè)信號處理函數(shù)執(zhí)行完畢之后,父進(jìn)程就可以繼續(xù)像往常一樣接受命令請求了。
在整個(gè)AOF后臺重寫過程中,只有信號處理函數(shù)執(zhí)行時(shí)會對服務(wù)器進(jìn)程(父進(jìn)程)造成 阻塞,在其他時(shí)候,AOF后臺重寫都不會阻塞父進(jìn)程,這將AOF重寫對服務(wù)器性能造成的影 響降到了最低。

Redis 混合持久化

Redis 還可以同時(shí)使用 AOF 持久化和 RDB 持久化。 在這種情況下, 當(dāng) Redis 重啟時(shí), 它會優(yōu)先使用 AOF 文件來還原數(shù)據(jù)集, 因?yàn)?AOF 文件保存的數(shù)據(jù)集通常比 RDB 文件所保存的數(shù)據(jù)集更完整。但是 AOF 恢復(fù)比較慢,Redis 4.0 推出了混合持久化。

混合持久化: 將 rdb 文件的內(nèi)容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是 自持久化開始到持久化結(jié)束 的這段時(shí)間發(fā)生的增量 AOF 日志,通常這部分 AOF 日志很小。

于是在 Redis 重啟的時(shí)候,可以先加載 RDB 的內(nèi)容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容