寫(xiě)在前面
Redis是一個(gè)內(nèi)存數(shù)據(jù)庫(kù),他將自己的數(shù)據(jù)存儲(chǔ)在內(nèi)存中,如果沒(méi)有辦法將內(nèi)存的數(shù)據(jù)存儲(chǔ)到磁盤,那么一旦發(fā)生宕機(jī),這些數(shù)據(jù)將會(huì)消失。這篇文章我們就講講Redis的持久化RDB。
1. 簡(jiǎn)介
RDB 全稱 Redis DataBase,可以將Redis在內(nèi)存中的數(shù)據(jù)庫(kù)狀態(tài)生成一個(gè)RDB文件保存在磁盤中。RDB既可以手動(dòng)執(zhí)行,也可以根據(jù)服務(wù)器配置選項(xiàng)定期執(zhí)行。
同樣的RDB文件也可以轉(zhuǎn)化成Redis數(shù)據(jù)庫(kù)狀態(tài),所以即使Redis宕機(jī)了,只要及時(shí)保存RDB在磁盤中,就能使用磁盤的RDB文件恢復(fù)Redis的數(shù)據(jù)庫(kù)狀態(tài)。
2. RDB的創(chuàng)建與加載
2.1 創(chuàng)建
有兩個(gè)Redis命令可以創(chuàng)建RDB文件,SAVE和BGSAVE
。這兩個(gè)命令的區(qū)別在于:SAVE命令會(huì)阻塞Redis服務(wù)器進(jìn)程,直到RDB文件創(chuàng)建完畢為止,在服務(wù)器進(jìn)程阻塞期間,服務(wù)器不能處理任何命令請(qǐng)求。
而B(niǎo)GSAVE是則會(huì)fork出一個(gè)子進(jìn)程,然后由這個(gè)子進(jìn)程負(fù)責(zé)創(chuàng)建RDB文件,主進(jìn)程繼續(xù)處理命令請(qǐng)求。
2.2 載入
RDB文件的載入工作是在服務(wù)器啟動(dòng)時(shí),自動(dòng)執(zhí)行的,所以Redis并沒(méi)有專門用于載入RDB文件的命令,只要Redis服務(wù)器在啟動(dòng)時(shí)檢測(cè)到RDB文件存在,就會(huì)自動(dòng)載入RDB文件。
這里注意一點(diǎn),我們知道除了RDB持久化,Redis還有AOF持久化,而AOF的更新頻率通常來(lái)說(shuō)是比RDB高的,也就意味著AOF中的數(shù)據(jù)會(huì)更新。
所以如果Redis服務(wù)器開(kāi)啟了AOF持久化功能,那么服務(wù)器會(huì)優(yōu)先使用AOF文件來(lái)還原數(shù)據(jù)庫(kù)狀態(tài)。只有在AOF關(guān)閉的時(shí)候,才會(huì)使用RDB。
而服務(wù)器在RDB載入期間也是處于阻塞狀態(tài),直到載入工作完成為止。
3. RDB文件結(jié)構(gòu)
- RDB文件的最開(kāi)頭是REDIS部分,這個(gè)部分的長(zhǎng)度為5字節(jié),保存著REDIS五個(gè)字符,通過(guò)這五個(gè)字符,程序可以在載入文件時(shí),快速檢查所載入的文件是否為RDB文件。
- db_version 長(zhǎng)度為4字節(jié),是一個(gè)字符串表示的整數(shù),
這個(gè)數(shù)值記錄了RDB的版本
,比如“0003” 就代表RDB文件為第3版。 - databases 中包含著零個(gè)或任意多個(gè)數(shù)據(jù)庫(kù),
以及各個(gè)數(shù)據(jù)庫(kù)中鍵值對(duì)數(shù)據(jù)。
- EOF常量的長(zhǎng)度為1字節(jié),這個(gè)常量標(biāo)志著RDB文件的讀取結(jié)束。當(dāng)程序讀到這里的時(shí)候,就意味著已經(jīng)結(jié)束了,所有的鍵值都已經(jīng)加載完了。
- check_sum 是一個(gè)8字節(jié)長(zhǎng)的無(wú)符號(hào)整數(shù),保存著一個(gè)校驗(yàn)和,這個(gè)校驗(yàn)和是根據(jù)前四個(gè)部分進(jìn)行計(jì)算得出來(lái)的。
服務(wù)器加載完數(shù)據(jù)后,會(huì)通過(guò)將載入的數(shù)據(jù)做運(yùn)算和check_sum做比較,來(lái)檢驗(yàn)RDB文件是否有損壞。
3.1 databases
一個(gè)RDB文件中的databases部分可以保存任意多個(gè)非空的數(shù)據(jù)庫(kù)。
而每個(gè)databases在RDB中都可以保存為以下這種形式
- SELECTDB 長(zhǎng)度為1字節(jié),類似于上面5字節(jié)的REDIS的作用,當(dāng)讀到這個(gè)值的時(shí)候,意味著下一位是讀一個(gè)數(shù)據(jù)庫(kù)號(hào)碼了
- db_number保存著一個(gè)數(shù)據(jù)庫(kù)號(hào)碼,比如database 0 這里存儲(chǔ)的就是 0
- key_value_pairs:保存了所有的鍵值對(duì)數(shù)據(jù)嘛,如果鍵值隊(duì)帶有過(guò)期時(shí)間,那么這個(gè)過(guò)期時(shí)間也會(huì)跟著一起保存在一起。
3.2 key value pairs
key_value_pairs保存了所有的鍵值對(duì)數(shù)據(jù)嘛,如果鍵值隊(duì)帶有過(guò)期時(shí)間,那么這個(gè)過(guò)期時(shí)間也會(huì)跟著一起保存在一起
。不帶過(guò)期時(shí)間的key就比如下面這個(gè):
TYPE 記錄了value的類型,長(zhǎng)度為1字節(jié),代表redis的所有類型,string、list、set、zset等等…
而帶有過(guò)期時(shí)間的key的結(jié)構(gòu)如下:
- 新增的 REPIRETIME_MS 長(zhǎng)度1字節(jié),這是告訴程序,
接下來(lái)要讀入一個(gè)以毫秒為單位的過(guò)期時(shí)間
。 - 而ms字段是一個(gè)8字節(jié)的長(zhǎng)的帶符號(hào)的整數(shù),
記錄一個(gè)以毫秒為單位的UNIX時(shí)間戳
,這個(gè)時(shí)間戳就是這個(gè)鍵值對(duì)的過(guò)期時(shí)間。