一個(gè)完整的RDB File二進(jìn)制展示
0000000 R E D I S 0 0 0 9 372 \t r e d i s
0000020 - v e r 005 6 . 0 . 6 372 \n r e d i
0000040 s - b i t s 300 @ 372 005 c t i m e 302
0000060 * O \n b 372 \b u s e d - m e m 302 300
0000100 3 \r \0 372 \f a o f - p r e a m b l
0000120 e 300 \0 376 \0 373 002 \0 \0 \n P S F _ K 1
0000140 _ P S F \n P S F _ V 1 _ P S F \0
0000160 \n P S F _ K 2 _ P S F \n P S F _
0000200 V 2 _ P S F 377 310 O 023 326 362 232 330 326
RDB文件中可以有什么际乘?
1. 常量字符串后室,如文件最開(kāi)頭的REDIS0009
2. 長(zhǎng)度編碼隘道,在需要長(zhǎng)度或者一個(gè)整數(shù)時(shí):先讀取2bit
00:后續(xù)6bit的值就是需要的整數(shù)
01:后續(xù)14bit的值就是需要的整數(shù)
10揭厚;舍棄6bit循头,再后續(xù)4字節(jié)的值就是需要的整數(shù)
11:特殊編碼孔祸,視后續(xù)6bit值而定屏富,情況如下:
000000:后續(xù)一個(gè)字節(jié)是整數(shù)
000001:后續(xù)兩個(gè)字節(jié)是整數(shù)
000010:后續(xù)四個(gè)字節(jié)是整數(shù)
000011:后續(xù)是一個(gè)LZF算法壓縮的字符串
3. 字符串編碼的值,在需要一個(gè)字符串編碼的值時(shí):
length prefixed string : length encoding + raw char[]
integer string : length encoding + X Byte, low 6bit of length encoding, if low 6bit is 0 => 1B 1 => 2B 2 => 4B
LZF compressed string : 11 000011
4. 解析引導(dǎo)頭字節(jié)养叛,占1B种呐,如下:
0xFA(372):輔助域,F(xiàn)A $string_encode_key $string_encode_val弃甥,其中string_encode_key string_encode_val都是字符串編碼的值
0xFB:字典和過(guò)期字典的大小爽室,F(xiàn)B $length_encode_int $length_encode_int,其中$length_encode_int $length_encode_in都是長(zhǎng)度編碼
0xFC:FC $unsigned_long $string_encode_key $encoded_value淆攻,這里$unsigned_long是定長(zhǎng)8B時(shí)間戳阔墩,$encoded_value就可是是各種其他類型了
0xFD:同0xFC,只是4B的秒級(jí)時(shí)間戳
0xFE:某個(gè)數(shù)據(jù)庫(kù)存檔的開(kāi)始瓶珊,F(xiàn)E $length_encode_int
0xFF:RDB文件即將結(jié)束戈擒,后面僅8B的check_sum了
以上解析引導(dǎo)頭字節(jié)后面所緊跟的字節(jié)的解析方式是固定的,但是還沒(méi)涉及到數(shù)據(jù)庫(kù)的key-value存儲(chǔ)艰毒,見(jiàn)下文:
5. 在0xFE $length_encode_int后,緊跟的就是該號(hào)對(duì)應(yīng)數(shù)據(jù)庫(kù)的key-value搜囱。所以RDB文件的大致結(jié)構(gòu)如下:
常量頭部
0xFA $string_encode_key $string_encode_val
0xFA $string_encode_key $string_encode_val
0xFA $string_encode_key $string_encode_val
0xFE $length_encode_int
ENCODE_TYPE $string_encode_key $encoded_value
ENCODE_TYPE $string_encode_key $encoded_value
ENCODE_TYPE $string_encode_key $encoded_value
0xFE $length_encode_int
ENCODE_TYPE $string_encode_key $encoded_value
ENCODE_TYPE $string_encode_key $encoded_value
ENCODE_TYPE $string_encode_key $encoded_value
0xFF + check_sum(8B)
各類型編碼解析
encode value丑瞧,數(shù)據(jù)庫(kù)鍵總是字符串編碼ENCODE_TYPE(1B),但是值卻是多樣的蜀肘,除了已經(jīng)介紹的字符串绊汹,還有如下:
#define RDB_TYPE_STRING 0
#define RDB_TYPE_LIST 1
#define RDB_TYPE_SET 2
#define RDB_TYPE_ZSET 3
#define RDB_TYPE_HASH 4
#define RDB_TYPE_HASH_ZIPMAP 9
#define RDB_TYPE_LIST_ZIPLIST 10
#define RDB_TYPE_SET_INTSET 11
#define RDB_TYPE_ZSET_ZIPLIST 12
#define RDB_TYPE_HASH_ZIPLIST 13
#define RDB_TYPE_LIST_QUICKLIST 14
#define RDB_TYPE_STREAM_LISTPACKS 15
RDB_TYPE_STRING => string:如你需要讀取一個(gè)string時(shí),那就按照[字符串編碼的值]去讀取就行了扮宠,例如:
例1 ==> 命令"set 123 456"產(chǎn)生的二進(jìn)制 \0 300 { 301 310 001 377西乖,由第一個(gè)字節(jié)\0可知狐榔,這是一個(gè)字符串鍵,
那么接下來(lái)應(yīng)該解析的是字符串key获雕,讀取一個(gè)字節(jié)300薄腻,其二進(jìn)制是11000000,這是特殊編碼的情形1届案,后續(xù)
的一個(gè)字節(jié)就是需要的庵楷,即{,其二進(jìn)制是123楣颠,這樣就解析出了key
接下來(lái)是value尽纽,讀取一個(gè)字節(jié)301,其二進(jìn)制是11000001童漩,這是特殊編碼的情形2弄贿,后續(xù)2個(gè)字節(jié)是需要的,
即001 377矫膨,轉(zhuǎn)換后即456
例2 ==> 命令"set k1 v1"產(chǎn)生的二進(jìn)制 \0 002 k 1 002 v 1差凹,由第一個(gè)字節(jié)\0可知,這是一個(gè)字符串鍵豆拨,
那么接下來(lái)應(yīng)該解析的是字符串key直奋,讀取一個(gè)字節(jié)002,其二進(jìn)制是00000010施禾,因?yàn)槲覀冃枰氖且粋€(gè)sting脚线,
由[字符串編碼的值]的方式可知,這是一個(gè)length-prefixed-string弥搞,002就是其長(zhǎng)度邮绿,那么往后讀取兩個(gè)字節(jié)k 1,
就是key="k1"攀例,同理可以得到其value="v1"
例3 ==> 命令"set k5 v555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555v"
產(chǎn)生的二進(jìn)制是 \0 002 k 5 303 \t @ \ 001 v 5 340 O \0 001 5 v船逮,對(duì)key的解析就不介紹了,下面介紹value粤铭,
讀取一個(gè)字節(jié)303挖胃,其二進(jìn)制是11000011,這是特殊編碼的情形4梆惯,也就是LZF壓縮的字符串酱鸭,其結(jié)構(gòu)如下:
len_after_compress + len_before_compress + string_after_compress
其中l(wèi)en_after_compress是經(jīng)過(guò)壓縮后的長(zhǎng)度,len_before_compress是壓縮前的長(zhǎng)度垛吗,string_after_compress是壓縮后的字符串凹髓,驗(yàn)證如下:
字節(jié)303后需要讀取len_after_compress了,先讀取一個(gè)字節(jié)\t怯屉,二進(jìn)制是00001001蔚舀,顯然是長(zhǎng)度編碼的情況1饵沧,解析后為9,
接下來(lái)解析len_before_compress赌躺,也是一個(gè)長(zhǎng)度狼牺,讀取一個(gè)字節(jié)@,其二進(jìn)制是01000000寿谴,顯然是長(zhǎng)度編碼的情況2锁右,所以接下來(lái)的14bit才是長(zhǎng)度
即01-000000 \,查ASCII表可得到長(zhǎng)度為92讶泰,正是v555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555v的長(zhǎng)度
最后便是壓縮后的字符串了咏瑟,其具體值已經(jīng)無(wú)法解釋(經(jīng)過(guò)壓縮了),但是長(zhǎng)度是len_after_compress=9字節(jié)痪署,即001 v 5 340 O \0 001 5 v
RDB_TYPE_LIST => list:有了string的例子码泞,理解list就簡(jiǎn)單多了,但是未經(jīng)試驗(yàn)(lpush后都是RDB_TYPE_LIST_QUICKLIST)狼犯,所以僅列出結(jié)構(gòu)
001 + key + value余寥,其中value是:length-encoding + N個(gè)string encoding
RDB_TYPE_SET => set:同list,也未經(jīng)試驗(yàn)(sadd后要么是intset悯森,要么是hashtable)
RDB_TYPE_ZSET => zset:
RDB_TYPE_HASH => hash:結(jié)構(gòu)如下
004 + key + size + field-pair宋舷,其中size是hash表中的元素個(gè)數(shù),后面跟隨size個(gè)field-pair
例1 ==> 命令"hmset h k1 v1 k2 v2 k3 v3 k4 v4 k5 v555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555v"
產(chǎn)生的二進(jìn)制如下瓢姻,開(kāi)干
0000280 004 001 h 005 002 k 4 002 v 4
0000300 002 k 1 002 v 1 002 k 2 002 v 2 002 k 3 002
0000320 v 3 002 k 5 303 \t @ \ 001 v 5 340 O \0 001
0000340 5 v 377
讀取004祝蝠,可知這是一個(gè)hash鍵,那么接下來(lái)就是讀取key幻碱,輕車熟路001 h绎狭,長(zhǎng)度為1的字符串h,
接下來(lái)是hash表中元素個(gè)數(shù)褥傍,讀取長(zhǎng)度儡嘶,easy,讀取一個(gè)字節(jié)005恍风,長(zhǎng)度編碼情況1蹦狂,即有5個(gè)元素,可知后續(xù)要連續(xù)讀取5組field-value朋贬,不再贅述鸥咖,僅列出如下:
002 k 4 002 v 4
002 k 1 002 v 1
002 k 2 002 v 2
002 k 3 002 v 3
002 k 5 303 \t @ \ 001 v 5 340 O \0 001 5 v
正是k1=v1 k2=v2 k3=v3 k4=v4,而k5就是字符串部分的例3
RDB_TYPE_HASH_ZIPMAP ==> zipmap:未出現(xiàn)過(guò)
RDB_TYPE_LIST_ZIPLIST ==> ziplist:結(jié)構(gòu)如下
012 + key + len + raw_string
其中raw_string = zlbyte(4B) + zltail(4B) + zllen(2B) + entrys + ... + EOF兄世,entry = pre_entry_len + encoding + content
RDB_TYPE_SET_INTSET ==> \v + key + size + encoding(4B) + len(4B) + len*integer
其中size是encoding + len + len*integer所占字節(jié)數(shù)
encoding是整數(shù)集合的編碼,即intset.encoding啊研,指導(dǎo)如何解析intset.contents
len是intset.len御滩,表示contents中裝的整數(shù)個(gè)數(shù)鸥拧,有了這兩個(gè)數(shù)就能解析intset.contents了
struct intset {
int encoding;
int len;
uint8_t contents[];
};
例1 ==> intset:命令"sadd sad 123 456"產(chǎn)生的二進(jìn)制如下: 實(shí)際從\v 003處開(kāi)始,
0000120 e 300 \0 376 \0 373 001 \0 \v 003 s a d \f 002 \0
0000140 \0 \0 002 \0 \0 \0 { \0 310 001 377 272 256 212 . X
0000160 244 + 244
讀取\v削解,其值為11富弦,即RDB_TYPE_SET_INTSET,接下來(lái)讀取key氛驮,003 s a d腕柜,
接下來(lái)讀取size,讀取一個(gè)字節(jié)\f矫废,其二進(jìn)制是00001100盏缤,長(zhǎng)度編碼情況1,得到12蓖扑,
接下來(lái)讀取encoding(4B 小端機(jī))唉铜,得到值為2,說(shuō)明intset中每個(gè)元素占2個(gè)字節(jié)律杠,
接下來(lái)讀取len(4B 小端機(jī))潭流,得到值為2,說(shuō)明intset中有兩個(gè)元素柜去,接下來(lái)按2字節(jié)截取灰嫉,得到{ \0 => 123 和 310 001 => 456
RDB_TYPE_ZSET_ZIPLIST ==> ziplist實(shí)現(xiàn)的zset:
RDB_TYPE_HASH_ZIPLIST ==> ziplist實(shí)現(xiàn)的hash:
RDB_TYPE_LIST_QUICKLIST ==> quicklist:
RDB_TYPE_STREAM_LISTPACKS ==> 未出現(xiàn)過(guò)
以下是打的草稿以及做的試驗(yàn)
總綱要:
- RDB中,因?yàn)閗ey永遠(yuǎn)是string嗓奢,而string的編碼只能是如下三種:
length-prefixed-string :002 k 1
8 16 32bit integer :300 {
LZF compressed string :303 \t @ \ 001 v 5 340 O \0 001 5 v - 各類value的編碼如下:
string:string的編碼同key
list:001 + key + value讼撒,其中value是:length-encoding + N個(gè)string encoding
set:同list,即002 + key + value
sorted set:003 + key + len + item-score + ...
hash:004 + key + key-pair-size + key-pair + ...
zipmap:
ziplist:012 + key + len + raw_string蔓罚,其中raw = zlbyte(4B) + zltail(4B) + zllen(2B) + entrys + ... + EOF
entry = pre_entry_len + encoding + content
intset:013 + key + size + encoding + len + leninteger
其中size是encoding + len + leninteger所占字節(jié)數(shù)
encoding是整數(shù)集合的編碼椿肩,即intset.encoding,指導(dǎo)如何解析intset.contents
len是intset.len豺谈,表示contents中裝的整數(shù)個(gè)數(shù)郑象,有了這兩個(gè)數(shù)就能解析intset.contents了
struct intset {
int encoding;
int len;
uint8_t contents[];
};
sorted set in ziplist:同ziplist
hashmap in ziplist:{"us" => "washington", "india" => "delhi"} => ["us", "washington", "india", "delhi"],然后就像存ziplist一樣
quicklist:016 + key + len + ZIPLIST + ZIPLIST + ...
set 123 456
\0 300 { 301 310 001 377
\0 => RDB_TYPE_STRING
300 => 11 000000 => 后續(xù)一個(gè)字節(jié)是整數(shù)茬末,即{ => 123厂榛,說(shuō)明這個(gè)string key里其實(shí)是一個(gè)整數(shù)
301 => 11 000001 => 后續(xù)兩個(gè)字節(jié)是整數(shù),即310 001 => 456
set k1 v1
\0 002 k 1 002 v 1
\0 => RDB_TYPE_STRING
002 k 1 => 長(zhǎng)度為2的字符串
002 v 1 => 長(zhǎng)度為2的字符串
數(shù)字編碼:讀取2bit
00:后續(xù)6bit是值
01:后續(xù)14bit是值
10:舍棄6bit丽惭,再后續(xù)4字節(jié)是值
特殊編碼
11:特殊編碼击奶,視后續(xù)6bit值而定,情況如下:
000000:后續(xù)一個(gè)字節(jié)是整數(shù)
000001:后續(xù)兩個(gè)字節(jié)是整數(shù)
000010:后續(xù)四個(gè)字節(jié)是整數(shù)
000011:后續(xù)是一個(gè)LZF算法壓縮的字符串
string encoding
length prefixed string : length encoding + raw char[]
integer string : length encoding + X Byte, low 6bit of length encoding, if low 6bit is 0 => 1B 1 => 2B 2 => 4B
LZF compressed string : 11 000011
hash encoding : 004 + key of hash encoded by length-prefixed-string + length-encoding + [string encoded key + value]
0000280 004 001 h 005 002 k 4 002 v 4
0000300 002 k 1 002 v 1 002 k 2 002 v 2 002 k 3 002
0000320 v 3 002 k 5 303 \t @ \ 001 v 5 340 O \0 001
0000340 5 v 377
004 => RDB_TYPE_HASH
001 h => hash的鍵明就叫h
005 => 5 => 5對(duì)key-pair
002 k 4 002 v 4
002 k 1 002 v 1
002 k 2 002 v 2
002 k 3 002 v 3
002 k 5 303 \t @ \ 001 v 5 340 O \0 001 5 v
著重解釋一下:key = k5责掏,其值是value = v555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555v
顯然這里進(jìn)行了LZF的壓縮柜砾,細(xì)看如下:
303 => 11 000011 => 特殊編碼,后續(xù)是一個(gè)LZF壓縮的字符串
\t => 9 => 壓縮后的長(zhǎng)度换衬,即001 v 5 340 O \0 001 5 v痰驱,這9個(gè)字節(jié)是經(jīng)過(guò)LZF壓縮后的值
@ \ => 01 000000 0101 1100 => 后14bit表示長(zhǎng)度 => 92 => value的長(zhǎng)度
372 => FA => 輔助字段
FA $string_encode_key $string_encode_val
解釋部分:
372 \t r e d i s - v e r 005 6 . 0 . 6
372標(biāo)識(shí)后續(xù)是輔助字段证芭,即將讀取兩個(gè)字符串,這兩個(gè)字符串都是length-prefix string担映,
\t對(duì)應(yīng)的十進(jìn)制是9废士,表示第一個(gè)字符串長(zhǎng)度為9,即redis-ver
005對(duì)應(yīng)十進(jìn)制是5蝇完,表示第二個(gè)字符串長(zhǎng)度為5官硝,即6.0.6
372 \n r e d i s - b i t s 300 @
\n對(duì)應(yīng)十進(jìn)制10,表示第一個(gè)字符串長(zhǎng)度為10短蜕,即redis-bits
300@對(duì)應(yīng)的二進(jìn)制是:1100 0000 0100 0000氢架,看前一個(gè)字節(jié)高兩位為11,次6位值為0忿危,表明后續(xù)是一個(gè)字節(jié)的整數(shù)达箍,即64
372 005 c t i m e 302 * O \n b
005 => 5 => ctime
302 => 1100 0010,后續(xù)4B是整數(shù)* O \n b铺厨,16進(jìn)制是2A4F0A62缎玫,轉(zhuǎn)為整數(shù)實(shí)際是:620A4F2A=1644842794,轉(zhuǎn)為時(shí)間是Mon Feb 14 20:46:34 CST 2022
372 \b u s e d - m e m 302 300 3 \r \0
\b => 8 => used-mem
302 => 1100 0010解滓,后續(xù)4B是整數(shù)300 3 \r \0 => C0030D00 => 000D03C0 => 852928赃磨,單位不詳
372 \f a o f - p r e a m b l e 300 \0
\f => 12 => aof-preamble
300 => 11 000000 后續(xù)一個(gè)字節(jié)是整數(shù)\0 => 0
373 => FB => ResizeDB Field
FB
$length_encode_int
$length_encode_int
373 002 \0
002 => 00 000010 => 2 => database hashtable size
\0 => 00 000000 => 0 => expire hashtable size
并且緊接著就是key_value_pairs部分了,key_value_pairs的格式是: TYPE KEY VALUE
字符串對(duì)象分析:
\0 \n P S F _ K 1 _ P S F \n P S F _ V 1 _ P S F
\0 \n P S F _ K 2 _ P S F \n P S F _ V 2 _ P S F
\0代表TYPE => RDB_TYPE_STRING => \n => 10 => PSF_K1_PSF
\0代表TYPE => RDB_TYPE_STRING => \n => 10 => PSF_V1_PSF
\0代表TYPE => RDB_TYPE_STRING => \n => 10 => PSF_K2_PSF
\0代表TYPE => RDB_TYPE_STRING => \n => 10 => PSF_V2_PSF
列表對(duì)象分析:
0000210 016 \b l
0000220 s t : n a m e 001 # # \0 \0 \0 033 \0 \0
0000240 \0 003 \0 \0 \a n a m e 1 2 3 \t 006 n a
0000260 m e 1 2 \b 005 n a m e 1 377
016 => 14 => RDB_TYPE_LIST_QUICKLIST
\b => 8 => lst:name
001 => 1 => RDB_TYPE_LIST
# => 35 => 接下來(lái)是一個(gè)35B的字符串洼裤,存儲(chǔ)時(shí)將ZIPLIST轉(zhuǎn)為字符串對(duì)象了
# \0 \0 \0 => 35(小端機(jī), ZLBYTE 4B) => ZIPLIST的ZLBYTE=35(ZIPLIST的結(jié)構(gòu)為 ZLBYTE ZLTAIL ZLLEN ENTRYS ZLEND)
033 \0 \0 \0 => 27(小端機(jī), ZLTAIL 4B) => 最后一個(gè)entry的首地址(即0000229(#位置) + 27 => 0000264(\b位置))
003 \0 => 3(小端機(jī), ZLLEN 2B) => 一共三個(gè)元素
\0 => 0 => 前一個(gè)entry的長(zhǎng)度邻辉,因?yàn)檫@是第一個(gè)entry,所以這個(gè)字段為0
\a => 7 => name123
\t => 9 => 前一個(gè)entry的長(zhǎng)度腮鞍,\0 \a n a m e 1 2 3值骇,指的是這一段的長(zhǎng)度
006 => 6 => name12
\b => 8 => 前一個(gè)entry的長(zhǎng)度,\t 006 n a m e 1 2移国,指的是這一段的長(zhǎng)度
005 => 5 => name1
將上述分節(jié)便是:
016
\blst:name
001 #
#\0\0\0 033\0\0\0 003\0 \0\aname123 \t006name12 \b005name1 377
374 => FC => 過(guò)期時(shí)間(ms),后續(xù)8B是時(shí)間戳
FC $unsigned_long
$value_type 1B
$string_encode_key
$encoded_value
下面這段不在此例中:通過(guò)命令得到set k1 v1 ex 1000
374 250 n 233 370 ~ 001 \0 \0 \0 002 k 1 002 v 1
8B => 250 n 233 370 ~ 001 \0 \0 => 過(guò)期時(shí)間戳
\0代表TYPE => RDB_TYPE_STRING => 002 => 2 => k1
\0代表TYPE => RDB_TYPE_STRING => 002 => 2 => v1
其格式為:FC TIMESTAMP TYPE KEY VALUE
375 => FD => 過(guò)期時(shí)間(s),后續(xù)4B是時(shí)間戳
FC $unsigned_int
$value_type 1B
$string_encode_key
$encoded_value
376 => FE => SELECT DB
FE $length_encoding
376 \0
select 0號(hào)DB
377 => FF => EOF
FF 8-byte-checksum