高頻面試題——Redis 的 RDB 持久化
良心公眾號
關(guān)注不迷路
01
RDB 持久化背景
在之前的 Redis 的五種基本類型(實(shí)戰(zhàn)篇)和 Redis 的五種基本類型(原理篇)兩篇文章中,我們討論了 Redis 支持的數(shù)據(jù)類型和底層原理,這是對于 Redis 這樣一個鍵值對數(shù)據(jù)庫最為基礎(chǔ)的。由于 Redis 的自身特性,在日常開發(fā)過程中,最常用的場景就是將其作為內(nèi)存數(shù)據(jù)庫來用。
眾所周知,內(nèi)存 (不包括非易失性內(nèi)存) 是不可靠的,如果不將數(shù)據(jù)持久化到磁盤,在服務(wù)器進(jìn)程退出之后,內(nèi)存中的數(shù)據(jù)將會丟失。
為了解決上述問題,Redis 提供了 RDB 持久化的能力,該能力可以將 Redis 在內(nèi)存中的數(shù)據(jù)持久化到磁盤,從而防止數(shù)據(jù)丟失。
02
RDB 持久化簡介
Redis 的 RDB 持久化是通過將某一時間點(diǎn)的數(shù)據(jù)庫狀態(tài)保存到 RDB 文件 (經(jīng)過壓縮的二進(jìn)制文件),并將 RDB 文件保存到磁盤中。通過 RDB 文件可以將數(shù)據(jù)還原至生成 RDB 文件的狀態(tài)。
03
RDB 持久化命令
Redis 提供了兩個命令用于生成 RDB 文件,這兩個命令分別是 SAVE 和 BGSAVE。
SAVE 命令
SAVE 命令是阻塞式的命令,即在 SAVE 命令執(zhí)行期間,Redis 服務(wù)器進(jìn)程會被阻塞,直到 SAVE 命令執(zhí)行完成,也即 RDB 文件創(chuàng)建完畢為止,在此期間,客戶端發(fā)送至 Redis 服務(wù)器的請求會被拒絕。
BGSAVE 命令
BGSAVE 命令是非阻塞式的命令,即執(zhí)行 BGSAVE 命令時,會 fork 子進(jìn)程來異步執(zhí)行 RDB 文件的創(chuàng)建工作,而父進(jìn)程(即 Redis 服務(wù)器進(jìn)程)可以繼續(xù)接收客戶端的請求并進(jìn)行相應(yīng)的處理。但值得一提的是,在 BGSAVE 命令執(zhí)行期間,并不是所有的客戶端請求服務(wù)器都會接收處理。為了避免產(chǎn)生競爭條件, SAVE 命以及新的 BGSAVE 命令,在 BGSAVE 命令執(zhí)行期間都會被拒絕。而出于對性能的考量, BGREWRITEAOF 命令 (用于異步執(zhí)行 AOF 文件重寫) 的執(zhí)行則會被延遲到 BGSAVE 命令執(zhí)行完畢之后。
自動持久化
既然 Redis 可以通過 RDB 文件實(shí)現(xiàn)持久化,那么一個比較自然的想法就是,讓 Redis 自動進(jìn)行持久化,而不用經(jīng)常顯式地執(zhí)行 SAVE 或者 BGSAVE 命令。Redis 提供了 save 選項(xiàng)用于配置服務(wù)器的自動持久化。配置方式如下:
// 100 秒內(nèi)至少進(jìn)行 5 次修改時觸發(fā)自動 RDBsave 100 5
而由于上文中已經(jīng)提到的,Redis 的 SAVE 命令是阻塞式的,在執(zhí)行期間服務(wù)器會拒絕客戶端的請求,因此,自動持久化背后的機(jī)制實(shí)際上使用的是對客戶端更加友好的 BGSAVE 命令。save 配置可以生效多條。
04
RDB 文件結(jié)構(gòu)剖析
在了解了 Redis 如何 RDB 持久化之后,是時候來看一下 RDB 文件的具體結(jié)構(gòu)了。只有了解了 RDB 文件的結(jié)構(gòu),才能更清楚 RDB 的持久化過程以及數(shù)據(jù)恢復(fù)過程。
RDB 文件結(jié)構(gòu)如下圖所示:

從上圖可以看出,Redis 的 RDB 文件結(jié)構(gòu)可以分為如下五個部分:
REDIS 是RDB 文件的標(biāo)識,在載入 RDB 文件時,可以通過該標(biāo)識快速判斷該文件是否是 RDB 文件。
db_version 是一個長度為 4 個字節(jié)的字符串,它對應(yīng)的整數(shù)值是 RDB 文件的版本號。
databases 保存了零個或任意多個數(shù)據(jù)庫,以及數(shù)據(jù)庫中的鍵值對。這一部分內(nèi)容是 RDB 文件的主體。由于該部分可以保存多個數(shù)據(jù)庫以及其中的鍵值對,因此,需要對這一部分內(nèi)容進(jìn)行進(jìn)一步的劃分。對于每一個數(shù)據(jù)庫,它內(nèi)部的結(jié)構(gòu)如下圖所示:

從上圖可以看出,databases 中的每一個數(shù)據(jù)庫內(nèi)部結(jié)構(gòu)可以分為如下三個部分:
SELECTDB 是一個長度為 1 個字節(jié)的標(biāo)識,每一個數(shù)據(jù)庫都以 SELECTDB 為起始,不同的數(shù)據(jù)庫之間依靠該標(biāo)識作為分割。
db_number 是數(shù)據(jù)庫的號碼,代表接下來要讀的是幾號數(shù)據(jù)庫。
key_value_pairs 是 db_number 數(shù)據(jù)庫中所有的鍵值對。
EOF 是一個長度為 1 個字節(jié)的標(biāo)識,用于表示 databases 內(nèi)容的結(jié)束。
check_sum 是一個長度為 8 個字節(jié)的無符號整數(shù),用于保存該 RDB 文件的校驗(yàn)和,依據(jù)該校驗(yàn)和來檢查和判斷 RDB 文件是否正常。
至此,我們已經(jīng)了解了 RDB 文件的基本結(jié)構(gòu),Redis 在執(zhí)行 SAVE 命令或者 BGSAVE 命令的時候,就會按照上述的文件結(jié)構(gòu),向磁盤中寫入相應(yīng)的 RDB 文件。
05
RDB 持久化的不可靠性
在了解了 Redis 的 RDB 持久化機(jī)制后,我們會發(fā)現(xiàn)一個問題——RDB 持久化的不可靠問題。這個問題也是面試官經(jīng)??疾斓囊稽c(diǎn)。
因?yàn)?RDB 文件的寫入并不是實(shí)時的,即便是在配置文件中配置了 save 屬性,在觸發(fā) BGSAVE 命令之前,并不會執(zhí)行 RDB 文件的寫入操作,如果 Redis 服務(wù)進(jìn)程意外退出,此時通過 RDB 文件僅僅能將數(shù)據(jù)庫狀態(tài)恢復(fù)到上一次 RDB 持久化的狀態(tài),在此之后所做的改動將會被丟失,這也就是 RDB 持久化不可靠的根源所在。
因此,對于數(shù)據(jù)丟失并不敏感的情況下,可以采用 RDB 持久化的方式。而對于數(shù)據(jù)丟失相對比較敏感的情況下,可以采用 Redis 的另外一種持久化的方式——AOF 持久化,在接下來的文章中,將會系統(tǒng)地介紹 AOF 持久化的內(nèi)容,敬請期待!
綜上所述,本文關(guān)于 Redis 的 RDB 持久化機(jī)制就總結(jié)到這里了。
歡迎關(guān)注【有理想的菜雞】公眾號,大家一起討論技術(shù),共同成長!
06
參考資料
《Redis 設(shè)計(jì)與實(shí)現(xiàn)》黃健宏 著
https://github.com/redis/redis

學(xué)習(xí) | 工作 | 分享
??關(guān)注“有理想的菜雞”
