概覽
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);
Body
DB Selector
FE開頭表示后跟表示DB Selector际邻;例如:
FE 00#FE表明數(shù)據(jù)庫的哪個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個字節(jié) $value-type #1個字節(jié)揖铜,表明數(shù)據(jù)類型:set,map等 $string-encoded-key #key值茴丰,字符串類型 $encoded-value #value,編碼方式和類型有關
-
expire為millisecond
FC $unsigned long #失效時間(毫秒),8個字節(jié) $value-type #數(shù)據(jù)類型,1個字節(jié) $string-encoded-key #key天吓,字符串類型 $encoded-value #value,編碼方式和類型有關
-
無expire
$value-type #數(shù)據(jù)類型,1個字節(jié) $string-encoded-key #key贿肩,字符串類型 $encoded-value #value,編碼方式和類型有關
Footer
FF #RDB文件的結束
8byte checksum #循環(huán)冗余校驗碼,Redis采用crc-64-jones算法龄寞,初始值為0
編碼算法說明
Length編碼
長度采用BigEndian格式存儲汰规,為無符號整數(shù)
- 如果以"00"開頭,那么接下來的6個bit表示長度物邑;
- 如果以“01”開頭溜哮,那么接下來的14個bit表示長度滔金;
- 如果以"10"開頭,該byte的剩余6bit廢棄茬射,接著讀入4個bytes表示長度(BigEndian)鹦蠕;
- 如果以"11"開頭,那么接下來的6個bit表示特殊的編碼格式在抛,一般用來存儲數(shù)字:
- 0表示用接下來的1byte表示長度
- 1表示用接下來的2bytes表示長度钟病;
- 2表示用接下來的4bytes表示長度;
String編碼
該編碼方式首先采用Length編碼 進行解析:
- 從上面的Length編碼知道刚梭,如果以"00","01","10"開頭肠阱,首先讀取長度;然后從接下來的內(nèi)容中讀取指定長度的字符朴读;
- 如果以"11"開頭屹徘,而且接下來的6個字節(jié)為“0”、“1”和“2”,那么直接讀取接下來的1衅金,2噪伊,4bytes做為字符串的內(nèi)容(實際上存儲的是數(shù)字,只不過按照字符串的格式存儲)氮唯;
- 如果以“11”開頭鉴吹,而且接下來的6個字節(jié)為"3",表明采用LZF壓縮字符串格式:
LZF編碼的解析步驟為:
- 首先采用Length編碼讀取壓縮后字符串的長度clen;
- 接著采用Length編碼讀取壓縮前的字符串長度;
- 讀取clen長度的字節(jié),并采用lzf算法解壓得到原始的字符串
Score編碼
- 讀取1個字節(jié)惩琉,如果為255豆励,則返回負無窮;
- 如果為254瞒渠,返回正無窮良蒸;
- 如果為253,返回非數(shù)字伍玖;
- 否則嫩痰,將該字節(jié)的值做為長度,讀取該長度的字節(jié)窍箍,將結果做為分值串纺;
Value編碼
Redis中的value編碼包括如下類型:
類型名稱 | 類型代碼 |
---|---|
String Encoding | 0 |
List Encoding | 1 |
Set Encoding | 2 |
Sorted Set Encoding | 3 |
Hash Encoding | 4 |
Zipmap Encoding | 9 |
Ziplist Encoding | 10 |
Intset Encoding | 11 |
Sorted Set in Ziplist Encoding | 12 |
Hashmap in Ziplist Encoding | 13 |
其中String編碼在前面已經(jīng)介紹過,接下來逐一介紹其他的9種編碼方式仔燕;
List
- 首先用Length編碼讀取List的長度lsize造垛;
- 采用String編碼讀取lsize個字符串
Set
同List
Sorted Set
- 首先用Length編碼讀取Sorted Set的長度zsize;
- 采用String編碼讀取字符串,采用Score編碼讀取分值晰搀;
- 循環(huán)讀取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:一個字節(jié)蠕蚜,Zipmap的大小悔橄;如果>=254,意味著zipmap的大小無法直接獲取到靶累,必須要遍歷整個zipmap才能得到大小癣疟;
- len:字符串長度挣柬,1或5個字節(jié)長度;如果第一個字節(jié)在0~252之間睛挚,那么長度為第一個字節(jié)邪蛔;如果為253,那么接下來的4個字節(jié)表示長度;254和255是無效值扎狱;
- free:1字節(jié),表明value空閑的字節(jié)數(shù)侧到;
- zmend:0xff,表示Zipmap的結尾;
Ziplist
采用String編碼讀取整個ziplist字符串淤击,字符串的格式為:
<zlbytes><zltail><zllen><entry><entry><zlend>
- zlbytes:4字節(jié)無符號整數(shù)匠抗,表示ziplist占用的總字節(jié)數(shù);
- zltail:4字節(jié)無符號整數(shù)(little endian),表示尾元素的偏移量遭贸;
- zllen:2字節(jié)無符號整數(shù)(little endian),表示ziplist中的元素個數(shù), 當元素個數(shù)大于65535時戈咳,無法用2字節(jié)表示心软,需要遍歷列表獲取元素個數(shù)壕吹;
- entry:ziplist中的元素;
- zlend:常量(0xff),表示ziplist的結尾删铃;
entry的格式:
<length-prev-entry><encoding><content>
- lenth-prev-entry:如果第一個字節(jié)<254,則用1bytes表示長度耳贬;否則則用接下來的4bytes(無符號整數(shù))表示長度;
- encoding
- "00"開頭:字符串,用接下來的6bit表示長度猎唁;
- "01"開頭:字符串咒劲,用接下來的14bit表示長度;
- "10"開頭:字符串,忽略本字節(jié)的6bit,用接下來的32bit表示長度诫隅;
- "11000000"開頭:整數(shù)腐魂,內(nèi)容為接下來的16bit;
- "11010000"開頭:整數(shù)逐纬,內(nèi)容為接下來的32bit蛔屹;
- "11100000"開頭:整數(shù),內(nèi)容為接下來的64bit豁生;
- "11110000"開頭:整數(shù)兔毒,內(nèi)容為接下來的24bit漫贞;
- "11111110"開頭:整數(shù),內(nèi)容為接下來的8bit育叁;
- "1111"開頭 :整數(shù)迅脐,內(nèi)容為接下來的4bit的值減去1;
- content
entry內(nèi)容豪嗽,它的長度通過前面的encoding確定谴蔑;
注意:元素長度、內(nèi)容長度等都是采用Little Endian編碼;
Intset
Intset是一個整數(shù)組成的二叉樹龟梦;當set的所有元素都是整形的時候树碱,Redis會采用該編碼進行存儲;Inset最大可以支持64bit的整數(shù)变秦,做為優(yōu)化成榜,如果整數(shù)可以用更少的字節(jié)數(shù)表示,Redis可能會用16~32bit來表示蹦玫;注意的是當插入一個長度不一樣的整數(shù)時赎婚,有可能會引起整個存儲結構的變化;
由于Intset是一個二叉樹樱溉,因此它的元素都是排序過的挣输;
采用String編碼讀取整個intset字符串,字符串的格式為:
<encoding><length-of-contents><contents>
- encoding:32bit的無符號整數(shù)福贞;可選值包括2撩嚼、4和8;表示inset中的每個整數(shù)占用的字節(jié)數(shù)挖帘;
- length-of-contents:32bit無符號整數(shù)完丽,表示Intset中包含的整數(shù)個數(shù);
- contents:整數(shù)數(shù)組拇舀,長度由length-of-contents決定逻族;
Sorted Set in Ziplist Encoding
采用Ziplist編碼,區(qū)別在于用兩個entry分別表示元素和分值骄崩;
Hashmap in Ziplist Encoding
采用Ziplist編碼聘鳞,區(qū)別在于用兩個entry分別表示key和value;
代碼樣例
代碼樣例請參考github上的例子redis-sync