RDB 是什么
Redis *.rdb
是內存的二進制文件
,通過 *.rdb 能夠完全回復 Redis 的運行狀態。

RDB 文件格式
詳細信息可參考:Redis RDB Dump File Format。
Header
RDB 文件的頭部占用 9bytes,前 5bytes 為 Magic String
, 后 4bytes 為版本號
;
52 45 44 49 53 #"REDIS", 就像 java 的 class 文件以 0xCAFEBABE 開頭一樣
30 30 30 36 #RDB 版本號,30 表示‘0’,版本號為 0006=6
注意:版本號是字符串而不是整型:
snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);
RDB_VERSION 詳細信息可參考:Redis RDB Version History
Body
DB Selector
FE 開頭表示后跟表示 DB Selector
,例如:
FE 00 #FE 表明數據庫的哪個 db,此處為 db0
注意:DB Selector 長度不固定,具體的編碼方式請參見后文的 Length 編碼。
AUX Fields
FA 開頭表示后跟 AUX Fields, 記錄生成 Dump 文件的 Redis 相關信息
,例如 redis-ver、redis-bits、used-mem、aof-preamble 和 repl-id 等。這些信息采用 String 編碼;
注意:redis3.0 版本的 RDB 版本號為 6,redis3.2 的版本號為 7;
Key-Value
key-value 有三種格式:
-
expire 為 second
FD $unsigned int #失效時間(秒),4 個字節 $value-type #1 個字節,表明數據類型:set,map 等 $string-encoded-key #key 值,字符串類型 $encoded-value #value, 編碼方式和類型有關
-
expire 為 millisecond
FC $unsigned long #失效時間(毫秒),8 個字節 $value-type #數據類型,1 個字節 $string-encoded-key #key,字符串類型 $encoded-value #value, 編碼方式和類型有關
-
無 expire
$value-type #數據類型,1 個字節 $string-encoded-key #key,字符串類型 $encoded-value #value, 編碼方式和類型有關
Footer
FF #RDB 文件的結束
8byte checksum #循環冗余校驗碼,Redis 采用 crc-64-jones 算法,初始值為 0
編碼算法說明
Length 編碼
長度采用 BigEndian 格式存儲,為無符號整數
- 如果以"00"開頭,那么接下來的 6 個 bit 表示長度;
- 如果以“01”開頭,那么接下來的 14 個 bit 表示長度;
- 如果以"10"開頭,該 byte 的剩余 6bit 廢棄,接着讀入 4 個 bytes 表示長度 (BigEndian);
- 如果以"11"開頭,那么接下來的 6 個 bit 表示特殊的編碼格式,一般用來存儲數字:
- 0 表示用接下來的 1byte 表示長度
- 1 表示用接下來的 2bytes 表示長度;
- 2 表示用接下來的 4bytes 表示長度;
String 編碼
該編碼方式首先采用 Length 編碼 進行解析:
- 從上面的
Length 編碼
知道,如果以"00","01","10"開頭,首先讀取長度;然后從接下來的內容中讀取指定長度的字符; - 如果以"11"開頭,而且接下來的 6 個字節為“0”、“1”和“2”, 那么直接讀取接下來的 1,2,4bytes 做為字符串的內容(實際上存儲的是數字,只不過按照字符串的格式存儲);
- 如果以“11”開頭,而且接下來的 6 個字節為"3", 表明采用 LZF 壓縮字符串格式:
LZF 編碼的解析步驟為:
- 首先采用
Length 編碼
讀取壓縮后字符串的長度clen
; - 接着采用
Length 編碼
讀取壓縮前的字符串長度; - 讀取 clen 長度的字節,並采用 lzf 算法解壓得到原始的字符串
Score 編碼
- 讀取 1 個字節,如果為 255,則返回負無窮;
- 如果為 254,返回正無窮;
- 如果為 253,返回非數字;
- 否則,將該字節的值做為長度,讀取該長度的字節,將結果做為分值;
Value 編碼
Redis 中的 value 編碼包括如下類型:
其中 String 編碼在前面已經介紹過,接下來逐一介紹其他的 9 種編碼方式;
List
- 首先用 Length 編碼讀取 List 的長度 lsize;
- 采用 String 編碼讀取 lsize 個字符串
Set
同 List
Sorted Set
- 首先用 Length 編碼讀取 Sorted Set 的長度 zsize;
- 采用 String 編碼讀取字符串,采用 Score 編碼讀取分值;
- 循環讀取 zsize 次;
Hash
- 采用 Length 編碼讀取 Hash 的大小 hsize;
- 采用 String 編碼讀取 2*hsize 的字符串,按照 key,value 的方式組裝成 Map
Zipmap
用於存儲 hashmap,Redis2.6 之后,該編碼被廢棄,轉而采用 Ziplist 編碼;
采用 String 編碼讀取整個 zipmap 字符串,hashmap 字符串的格式為:
<zmlen><len>"foo"<len><free>"bar"<len>"hello"<len><free>"world"<zmend>
- zmlen: 一個字節,Zipmap 的大小;如果>=254, 意味着 zipmap 的大小無法直接獲取到,必須要遍歷整個 zipmap 才能得到大小;
- len: 字符串長度,1 或 5 個字節長度;如果第一個字節在 0~252 之間,那么長度為第一個字節;如果為 253, 那么接下來的 4 個字節表示長度;254 和 255 是無效值;
- free:1 字節,表明 value 空閑的字節數;
- zmend:0xff, 表示 Zipmap 的結尾;
Ziplist
采用 String 編碼讀取整個 ziplist 字符串,字符串的格式為:
<zlbytes><zltail><zllen><entry><entry><zlend>
- zlbytes:4 字節無符號整數,表示 ziplist 占用的總字節數;
- zltail:4 字節無符號整數 (little endian), 表示尾元素的偏移量;
- zllen:2 字節無符號整數 (little endian), 表示 ziplist 中的元素個數,當元素個數大於 65535 時,無法用 2 字節表示,需要遍歷列表獲取元素個數;
- entry:ziplist 中的元素;
- zlend: 常量 (0xff), 表示 ziplist 的結尾;
entry 的格式:
<length-prev-entry><encoding><content>
- lenth-prev-entry: 如果第一個字節<254, 則用 1bytes 表示長度;否則則用接下來的 4bytes(無符號整數)表示長度;
- encoding
- "00"開頭:字符串,用接下來的 6bit 表示長度;
- "01"開頭:字符串,用接下來的 14bit 表示長度;
- "10"開頭:字符串,忽略本字節的 6bit, 用接下來的 32bit 表示長度;
- "11000000"開頭:整數,內容為接下來的 16bit;
- "11010000"開頭:整數,內容為接下來的 32bit;
- "11100000"開頭:整數,內容為接下來的 64bit;
- "11110000"開頭:整數,內容為接下來的 24bit;
- "11111110"開頭:整數,內容為接下來的 8bit;
- "1111"開頭 :整數,內容為接下來的 4bit 的值減去 1;
- content
entry 內容,它的長度通過前面的 encoding 確定;
注意:元素長度、內容長度等都是采用 Little Endian 編碼;
Intset
Intset 是一個整數組成的二叉樹;當 set 的所有元素都是整形的時候,Redis 會采用該編碼進行存儲;Inset 最大可以支持 64bit 的整數,做為優化,如果整數可以用更少的字節數表示,Redis 可能會用 16~32bit 來表示;注意的是當插入一個長度不一樣的整數時,有可能會引起整個存儲結構的變化;
由於 Intset 是一個二叉樹,因此它的元素都是排序過的;
采用 String 編碼讀取整個 intset 字符串,字符串的格式為:
<encoding><length-of-contents><contents>
- encoding:32bit 的無符號整數;可選值包括 2、4 和 8;表示 inset 中的每個整數占用的字節數;
- length-of-contents:32bit 無符號整數,表示 Intset 中包含的整數個數;
- contents: 整數數組,長度由 length-of-contents 決定;
Sorted Set in Ziplist Encoding
采用 Ziplist 編碼,區別在於用兩個 entry 分別表示元素和分值;
Hashmap in Ziplist Encoding
采用 Ziplist 編碼,區別在於用兩個 entry 分別表示 key 和 value;
實際例子
本篇文章在本地安裝並啟動 Redis 服務,保存一個 string 類型的字符串,save 之后查看保存的 rdb 文件的二進制。
安裝、啟動 Redis
下載見:Redis Download
啟動 Redis server:
src/redis-server&
啟動一個 Redis client:
src/redis-cli
保存字符串
127.0.0.1:6379> set name yano
OK
保存 RDB 文件
127.0.0.1:6379> save
80277:M 15 Feb 2022 10:51:07.308 * DB saved on disk
OK
在剛執行 redis-cli 的目錄下,就生成了 rdb 文件,文件名是 dump.rdb。
分析 RDB 文件
使用 hexedit
命令分析 dump.rdb
文件:
hexedit dump.rdb
dump.rdb 文件內容如下:
本篇文章只是分析 rdb 文件的基本結構和格式,只保存了一個最基礎的 string。(圖畫了一個小時😁)RDB 這塊的 Redis 源碼就不分析了,基本上都是按照這個結構來的。
參考鏈接
Redis 源碼簡潔剖析系列
Java 編程思想-最全思維導圖-GitHub 下載鏈接,需要的小伙伴可以自取~
原創不易,希望大家轉載時請先聯系我,並標注原文鏈接。