轉載自 Redis RDB文件格式全解析
點評
這篇文章作爲對RDB理解的教程文章,對RDB文件的原理理解有助於進行Redis高階應用的設計與開發。
文章轉自:http://blog.nosqlfan.com/html/3734.html 作者:@nosqlfan
RDB文件是Redis持久化的一種方式,Redis通過制定好的策略,按期將內存中的數據以鏡像的形式轉存到RDB文件中。那麼RDB文件內部格式是什麼樣的呢,Redis又做了哪些工作讓RDB能夠更快的dump和加載呢,下面我們深入RDB文件,來看一看其內部結構。首先我們來看一個RDB文件的概況圖:
----------------------------# RDB文件是二進制的,所以並不存在回車換行來分隔一行一行.
52 45 44 49 53 # 以字符串 "REDIS" 開頭
30 30 30 33 # RDB 的版本號,大端存儲,比如左邊這個表示版本號爲0003
----------------------------
FE 00 # FE = FE表示數據庫編號,Redis支持多個庫,以數字編號,這裏00表示第0個數據庫
----------------------------# Key-Value 對存儲開始了
FD $length-encoding # FD 表示過期時間,過期時間是用 length encoding 編碼存儲的,後面會講到
$value-type # 1 個字節用於表示value的類型,比如set,hash,list,zset等
$string-encoded-key # Key 值,通過string encoding 編碼,同樣後面會講到
$encoded-value # Value值,根據不同的Value類型採用不同的編碼方式
----------------------------
FC $length-encoding # FC 表示毫秒級的過期時間,後面的具體時間用length encoding編碼存儲
$value-type # 同上,也是一個字節的value類型
$string-encoded-key # 同樣是以 string encoding 編碼的 Key值
$encoded-value # 同樣是以對應的數據類型編碼的 Value 值
----------------------------
$value-type # 下面是沒有過期時間設置的 Key-Value對,爲防止衝突,數據類型不會以 FD, FC, FE, FF 開頭
$string-encoded-key
$encoded-value
----------------------------
FE $length-encoding # 下一個庫開始,庫的編號用 length encoding 編碼
----------------------------
... # 繼續存儲這個數據庫的 Key-Value 對
FF ## FF:RDB文件結束的標誌
下面我們對上面的內容進行詳細講解
Magic Number
第一行就不用講了,REDIS字符串用於標識是Redis的RDB文件
版本號
用了4個字節存儲版本號,以大端(big endian)方式存儲和讀取
數據庫編號
以一個字節的0xFE開頭,後面存儲數據庫的具體編號,數據庫的編號是一個數字,通過 “Length Encoding” 方式編碼存儲,“Length Encoding” 我們後面會講到。
Key-Value值對
值對包括下面四個部分 1. Key 過期時間,這一項是可有可無的 2. 一個字節表示value的類型 3. Key的值,Key都是字符串,通過 “Redis String Encoding” 來保存 4. Value的值,通過 “Redis Value Encoding” 來根據不同的數據類型做不同的存儲
Key過期時間
過期時間由 0xFD 或 0xFC開頭用於標識,分別表示秒級的過期時間和毫秒級的過期時間,後面的具體時間是一個UNIX時間戳,秒級或毫秒級的。具體時間戳的值通過“Redis Length Encoding” 編碼存儲。在導入RDB文件的過程中,會通過過期時間判斷是否已過期並需要忽略。
Value類型
Value類型用一個字節進行存儲,目前包括以下一些值:
-
0 = “String Encoding”
-
1 = “List Encoding”
-
2 = “Set Encoding”
-
3 = “Sorted Set Encoding”
-
4 = “Hash Encoding”
-
9 = “Zipmap Encoding”
-
10 = “Ziplist Encoding”
-
11 = “Intset Encoding”
-
12 = “Sorted Set in Ziplist Encoding”
Key
Key值就是簡單的 “String Encoding” 編碼,具體可以看後面的描述
Value
上面列舉了Value的9種類型,實際上可以分爲三大類
-
type = 0, 簡單字符串
-
type 爲 9, 10, 11 或 12, value字符串在讀取出來後需要先解壓
-
type 爲 1, 2, 3 或 4, value是字符串序列,這一系列的字符串用於構建list,set,hash 和 zset 結構
Length Encoding
上面說了很多 Length Encoding ,現在就爲大家講解。可能你會說,長度用一個int存儲不就行了嗎?但是,通常我們使用到的長度可能都並不大,一個int 4個字節是否有點浪費呢。所以Redis採用了變長編碼的方法,將不同大小的數字編碼成不同的長度。
-
首先在讀取長度時,會讀一個字節的數據,其中前兩位用於進行變長編碼的判斷
-
如果前兩位是 0 0,那麼下面剩下的 6位就表示具體長度
-
如果前兩位是 0 1,那麼會再讀取一個字節的數據,加上前面剩下的6位,共14位用於表示具體長度
-
如果前兩位是 1 0,那麼剩下的 6位就被廢棄了,取而代之的是再讀取後面的4 個字節用於表示具體長度
-
如果前兩位是 1 1,那麼下面的應該是一個特殊編碼,剩下的 6位用於標識特殊編碼的種類。特殊編碼主要用於將數字存成字符串,或者編碼後的字符串。具體見 “String Encoding”
這樣做有什麼好處呢,實際就是節約空間:
-
0 – 63的數字只需要一個字節進行存儲
-
而64 – 16383 的數字只需要兩個字節進行存儲
-
16383 - 2^32 -1 的數字只需要用5個字節(1個字節的標識加4個字節的值)進行存儲
String Encoding
Redis的 String Encoding 是二進制安全的,也就是說他沒有任何特殊分隔符用於分隔各個值,你可以在裏面存儲任何東西。它就是一串字節碼。下面是 String Encoding 的三種類型
-
長度編碼的字符串
-
數字替代字符串:8位,16位或者32位的數字
-
LZF 壓縮的字符串
長度編碼字符串
長度編碼字符串是最簡單的一種類型,它由兩部分組成,一部分是用 “Length Encoding” 編碼的字符串長度,第二部分是具體的字節碼。
數字替代字符串
上面說到過 Length Encoding 的特殊編碼,就在這裏用上了。所以數字替代字符串是以 1 1 開頭的,然後讀取這個字節剩下的6 位,根據不同的值標識不同的數字類型:
-
0 表示下面是一個8 位的數字
-
1 表示下面是一個16 位的數字
-
2 表示下面是一個32 位的數字
LZF壓縮字符串
和數據替代字符串一樣,它也是以1 1 開頭的,然後剩下的6 位如果值爲4,那麼就表示它是一個壓縮字符串。壓縮字符串解析規則如下:
-
首先按 Length Encoding 規則讀取壓縮長度 clen
-
然後按 Length Encoding 規則讀取非壓縮長度
-
再讀取第二個 clen
-
獲取到上面的三個信息後,再通過LZF算法解碼後面clen長度的字節碼
List Encoding
Redis List 結構在RDB文件中的存儲,是依次存儲List中的各個元素的。其結構如下:
-
首先按 Length Encoding 讀取這個List 的長度 size
-
然後讀取 size個 String Encoding的值
-
然後再用這些讀到的 size 個值重新構建 List就完成了
Set Encoding
Set結構和List結構一樣,也是依次存儲各個元素的
Sorted Set Encoding
todo
Hash Encoding
-
首先按 Length Encoding 讀出hash 結構的大小 size
-
然後讀取2×size 個 String Encoding的字符串(因爲一個hash項包括key和value兩項)
-
將上面讀取到的2×size 個字符串解析爲hash 和key 和 value
-
然後將上面的key value對存儲到hash結構中