99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Redis RDB文件結(jié)構(gòu)

2018-08-02 14:51 更新

在本章之前的內(nèi)容中, 我們介紹了 Redis 服務(wù)器保存和載入 RDB 文件的方法, 在這一節(jié), 我們將對 RDB 文件本身進(jìn)行介紹, 并詳細(xì)說明文件各個部分的結(jié)構(gòu)和意義。

圖 IMAGE_RDB_STRUCT_OVERVIEW 展示了一個完整 RDB 文件所包含的各個部分。

注意

為了方便區(qū)分變量、數(shù)據(jù)、常量, 圖 IMAGE_RDB_STRUCT_OVERVIEW 中用全大寫單詞標(biāo)示常量, 用全小寫單詞標(biāo)示變量和數(shù)據(jù)。

本章展示的所有 RDB 文件結(jié)構(gòu)圖都遵循這一規(guī)則。

RDB 文件的最開頭是 REDIS 部分, 這個部分的長度為 5 字節(jié), 保存著 "REDIS" 五個字符。 通過這五個字符, 程序可以在載入文件時, 快速檢查所載入的文件是否 RDB 文件。

注意

因為 RDB 文件保存的是二進(jìn)制數(shù)據(jù), 而不是 C 字符串, 為了簡便起見, 我們用 "REDIS" 符號代表 'R' 、 'E' 、 'D' 、 'I' 、 'S'五個字符, 而不是帶 '\0' 結(jié)尾符號的 C 字符串 'R' 、 'E' 、 'D' 、 'I' 、 'S' 、 '\0' 。

本章介紹的所有內(nèi)容,以及展示的所有 RDB 文件結(jié)構(gòu)圖都遵循這一規(guī)則。

db_version 長度為 4 字節(jié), 它的值是一個字符串表示的整數(shù), 這個整數(shù)記錄了 RDB 文件的版本號, 比如 "0006" 就代表 RDB 文件的版本為第六版。 本章只介紹第六版 RDB 文件的結(jié)構(gòu)。

databases 部分包含著零個或任意多個數(shù)據(jù)庫, 以及各個數(shù)據(jù)庫中的鍵值對數(shù)據(jù):

  • 如果服務(wù)器的數(shù)據(jù)庫狀態(tài)為空(所有數(shù)據(jù)庫都是空的), 那么這個部分也為空, 長度為 0 字節(jié)。
  • 如果服務(wù)器的數(shù)據(jù)庫狀態(tài)為非空(有至少一個數(shù)據(jù)庫非空), 那么這個部分也為非空, 根據(jù)數(shù)據(jù)庫所保存鍵值對的數(shù)量、類型和內(nèi)容不同, 這個部分的長度也會有所不同。

EOF 常量的長度為 1 字節(jié), 這個常量標(biāo)志著 RDB 文件正文內(nèi)容的結(jié)束, 當(dāng)讀入程序遇到這個值的時候, 它知道所有數(shù)據(jù)庫的所有鍵值對都已經(jīng)載入完畢了。

check_sum 是一個 8 字節(jié)長的無符號整數(shù), 保存著一個校驗和, 這個校驗和是程序通過對 REDIS 、 db_version 、 databases 、 EOF 四個部分的內(nèi)容進(jìn)行計算得出的。 服務(wù)器在載入 RDB 文件時, 會將載入數(shù)據(jù)所計算出的校驗和與 check_sum 所記錄的校驗和進(jìn)行對比, 以此來檢查 RDB 文件是否有出錯或者損壞的情況出現(xiàn)。

作為例子, 圖 IMAGE_RDB_WITH_EMPTY_DATABASE 展示了一個 databases 部分為空的 RDB 文件: 文件開頭的 "REDIS" 表示這是一個 RDB 文件, 之后的 "0006" 表示這是第六版的 RDB 文件, 因為 databases 為空, 所以版本號之后直接跟著 EOF 常量, 最后的 6265312314761917404是文件的校驗和。

databases 部分

一個 RDB 文件的 databases 部分可以保存任意多個非空數(shù)據(jù)庫。

比如說, 如果服務(wù)器的 0 號數(shù)據(jù)庫和 3 號數(shù)據(jù)庫非空, 那么服務(wù)器將創(chuàng)建一個如圖 IMAGE_RDB_WITH_TWO_DB 所示的 RDB 文件, 圖中的database 0 代表 0 號數(shù)據(jù)庫中的所有鍵值對數(shù)據(jù), 而 database 3 則代表 3 號數(shù)據(jù)庫中的所有鍵值對數(shù)據(jù)。

每個非空數(shù)據(jù)庫在 RDB 文件中都可以保存為 SELECTDB 、 db_number 、 key_value_pairs 三個部分, 如圖 IMAGE_DATABASE_STRUCT_OF_RDB 所示。

SELECTDB 常量的長度為 1 字節(jié), 當(dāng)讀入程序遇到這個值的時候, 它知道接下來要讀入的將是一個數(shù)據(jù)庫號碼。

db_number 保存著一個數(shù)據(jù)庫號碼, 根據(jù)號碼的大小不同, 這個部分的長度可以是 1 字節(jié)、 2 字節(jié)或者 5 字節(jié)。 當(dāng)程序讀入 db_number 部分之后, 服務(wù)器會調(diào)用 SELECT 命令, 根據(jù)讀入的數(shù)據(jù)庫號碼進(jìn)行數(shù)據(jù)庫切換, 使得之后讀入的鍵值對可以載入到正確的數(shù)據(jù)庫中。

key_value_pairs 部分保存了數(shù)據(jù)庫中的所有鍵值對數(shù)據(jù), 如果鍵值對帶有過期時間, 那么過期時間也會和鍵值對保存在一起。 根據(jù)鍵值對的數(shù)量、類型、內(nèi)容、以及是否有過期時間等條件的不同, key_value_pairs 部分的長度也會有所不同。

作為例子, 圖 IMAGE_EXAMPLE_OF_DB 展示了 RDB 文件中, 0 號數(shù)據(jù)庫的結(jié)構(gòu)。

另外, 圖 IMAGE_RDB_WITH_DB_0_AND_DB_3 則展示了一個完整的 RDB 文件, 文件中包含了 0 號數(shù)據(jù)庫和 3 號數(shù)據(jù)庫。

key_value_pairs 部分

RDB 文件中的每個 key_value_pairs 部分都保存了一個或以上數(shù)量的鍵值對, 如果鍵值對帶有過期時間的話, 那么鍵值對的過期時間也會被保存在內(nèi)。

不帶過期時間的鍵值對在 RDB 文件中對由 TYPE 、 key 、 value 三部分組成, 如圖 IMAGE_KEY_WITHOUT_EXPIRE_TIME 所示。

TYPE 記錄了 value 的類型, 長度為 1 字節(jié), 值可以是以下常量的其中一個:

  • REDIS_RDB_TYPE_STRING
  • REDIS_RDB_TYPE_LIST
  • REDIS_RDB_TYPE_SET
  • REDIS_RDB_TYPE_ZSET
  • REDIS_RDB_TYPE_HASH
  • REDIS_RDB_TYPE_LIST_ZIPLIST
  • REDIS_RDB_TYPE_SET_INTSET
  • REDIS_RDB_TYPE_ZSET_ZIPLIST
  • REDIS_RDB_TYPE_HASH_ZIPLIST

以上列出的每個 TYPE 常量都代表了一種對象類型或者底層編碼, 當(dāng)服務(wù)器讀入 RDB 文件中的鍵值對數(shù)據(jù)時, 程序會根據(jù) TYPE 的值來決定如何讀入和解釋 value 的數(shù)據(jù)。

key 和 value 分別保存了鍵值對的鍵對象和值對象:

  • 其中 key 總是一個字符串對象, 它的編碼方式和 REDIS_RDB_TYPE_STRING 類型的 value 一樣。 根據(jù)內(nèi)容長度的不同, key 的長度也會有所不同。
  • 根據(jù) TYPE 類型的不同, 以及保存內(nèi)容長度的不同, 保存 value 的結(jié)構(gòu)和長度也會有所不同, 本節(jié)稍后會詳細(xì)說明每種 TYPE 類型的value 結(jié)構(gòu)保存方式。

帶有過期時間的鍵值對在 RDB 文件中的結(jié)構(gòu)如圖 IMAGE_KEY_WITH_EXPIRE_TIME 所示。

帶有過期時間的鍵值對中的 TYPE 、 key 、 value 三個部分的意義, 和前面介紹的不帶過期時間的鍵值對的 TYPE 、 key 、 value 三個部分的意義完全相同, 至于新增的 EXPIRETIME_MS 和 ms , 它們的意義如下:

  • EXPIRETIME_MS 常量的長度為 1 字節(jié), 它告知讀入程序, 接下來要讀入的將是一個以毫秒為單位的過期時間。
  • ms 是一個 8 字節(jié)長的帶符號整數(shù), 記錄著一個以毫秒為單位的 UNIX 時間戳, 這個時間戳就是鍵值對的過期時間。

作為例子, 圖 IMAGE_EXAMPLE_OF_KEY_WITHOUT_EXPIRE_TIME 展示了一個沒有過期時間的字符串鍵值對。

圖 IMAGE_EXAMPLE_OF_KEY_WITH_EXPIRE_TIME 展示了一個帶有過期時間的集合鍵值對, 其中鍵的過期時間為 1388556000000 (2014 年 1 月 1 日零時)。

value 的編碼

RDB 文件中的每個 value 部分都保存了一個值對象, 每個值對象的類型都由與之對應(yīng)的 TYPE 記錄, 根據(jù)類型的不同, value 部分的結(jié)構(gòu)、長度也會有所不同。

在接下來的各個小節(jié)中, 我們將分別介紹各種不同類型的值對象在 RDB 文件中的保存結(jié)構(gòu)。

注意

本節(jié)接下來說到的各種 REDIS_ENCODING_* 編碼曾經(jīng)在《對象》一章中介紹過, 如果忘記了可以去回顧一下。

字符串對象

如果 TYPE 的值為 REDIS_RDB_TYPE_STRING , 那么 value 保存的就是一個字符串對象, 字符串對象的編碼可以是 REDIS_ENCODING_INT 或者REDIS_ENCODING_RAW 。

如果字符串對象的編碼為 REDIS_ENCODING_INT , 那么說明對象中保存的是長度不超過 32 位的整數(shù), 這種編碼的對象將以圖 IMAGE_INT_ENCODING_STRING 所示的結(jié)構(gòu)保存。

其中, ENCODING 的值可以是 REDIS_RDB_ENC_INT8 、 REDIS_RDB_ENC_INT16 或者 REDIS_RDB_ENC_INT32 三個常量的其中一個, 它們分別代表 RDB 文件使用 8 位(bit)、 16 位或者 32 位來保存整數(shù)值 integer 。

舉個例子, 如果字符串對象中保存的是可以用 8 位來保存的整數(shù) 123 , 那么這個對象在 RDB 文件中保存的結(jié)構(gòu)將如圖 IMAGE_EXAMPLE_OF_INT_ENCODING_STRING 所示。

如果字符串對象的編碼為 REDIS_ENCODING_RAW , 那么說明對象所保存的是一個字符串值, 根據(jù)字符串長度的不同, 有壓縮和不壓縮兩種方法來保存這個字符串:

  • 如果字符串的長度小于等于 20 字節(jié), 那么這個字符串會直接被原樣保存。
  • 如果字符串的長度大于 20 字節(jié), 那么這個字符串會被壓縮之后再保存。

注意

以上兩個條件是在假設(shè)服務(wù)器打開了 RDB 文件壓縮功能的情況下進(jìn)行的, 如果服務(wù)器關(guān)閉了 RDB 文件壓縮功能, 那么 RDB 程序總以無壓縮的方式保存字符串值。

具體信息可以參考 redis.conf 文件中關(guān)于 rdbcompression 選項的說明。

對于沒有被壓縮的字符串, RDB 程序會以圖 IMAGE_NON_COMPRESS_STRING 所示的結(jié)構(gòu)來保存該字符串。

其中, string 部分保存了字符串值本身,而 len 保存了字符串值的長度。

對于壓縮后的字符串, RDB 程序會以圖 IMAGE_COMPRESSED_STRING 所示的結(jié)構(gòu)來保存該字符串。

其中, REDIS_RDB_ENC_LZF 常量標(biāo)志著字符串已經(jīng)被 LZF 算法(http://liblzf.plan9.de)壓縮過了, 讀入程序在碰到這個常量時, 會根據(jù)之后的 compressed_len 、 origin_len 和 compressed_string 三部分, 對字符串進(jìn)行解壓縮: 其中 compressed_len 記錄的是字符串被壓縮之后的長度, 而 origin_len 記錄的是字符串原來的長度, compressed_string 記錄的則是被壓縮之后的字符串。

圖 IMAGE_EXAMPLE_OF_NON_COMPRESS_STRING 展示了一個保存無壓縮字符串的例子, 其中字符串的長度為 5 , 字符串的值為 "hello" 。

圖 IMAGE_EXAMPLE_OF_COMPRESS_STRING 展示了一個壓縮后的字符串示例, 從圖中可以看出, 字符串原本的長度為 21 , 壓縮之后的長度為6 , 壓縮之后的字符串內(nèi)容為 "?aa???" , 其中 ? 代表的是無法用字符串形式打印出來的字節(jié)。

列表對象

如果 TYPE 的值為 REDIS_RDB_TYPE_LIST , 那么 value 保存的就是一個 REDIS_ENCODING_LINKEDLIST 編碼的列表對象, RDB 文件保存這種對象的結(jié)構(gòu)如圖 IMAGE_LINKEDLIST_ENCODING_LIST 所示。

list_length 記錄了列表的長度, 它記錄列表保存了多少個項(item), 讀入程序可以通過這個長度知道自己應(yīng)該讀入多少個列表項。

圖中以 item 開頭的部分代表列表的項, 因為每個列表項都是一個字符串對象, 所以程序會以處理字符串對象的方式來保存和讀入列表項。

作為示例, 圖 IMAGE_EXAMPLE_OF_LINKEDLIST_ENCODING_LIST 展示了一個包含三個元素的列表。

結(jié)構(gòu)中的第一個數(shù)字 3 是列表的長度, 之后跟著的分別是第一個列表項、第二個列表項和第三個列表項, 其中:

  • 第一個列表項的長度為 5 , 內(nèi)容為字符串 "hello" 。
  • 第二個列表項的長度也為 5 , 內(nèi)容為字符串 "world" 。
  • 第三個列表項的長度為 1 , 內(nèi)容為字符串 "!" 。

集合對象

如果 TYPE 的值為 REDIS_RDB_TYPE_SET , 那么 value 保存的就是一個 REDIS_ENCODING_HT 編碼的集合對象, RDB 文件保存這種對象的結(jié)構(gòu)如圖 IMAGE_HT_ENCODING_SET 所示。

其中, set_size 是集合的大小, 它記錄集合保存了多少個元素, 讀入程序可以通過這個大小知道自己應(yīng)該讀入多少個集合元素。

圖中以 elem 開頭的部分代表集合的元素, 因為每個集合元素都是一個字符串對象, 所以程序會以處理字符串對象的方式來保存和讀入集合元素。

作為示例, 圖 IMAGE_EXAMPLE_OF_HT_SET 展示了一個包含四個元素的集合。

結(jié)構(gòu)中的第一個數(shù)字 4 記錄了集合的大小, 之后跟著的是集合的四個元素:

  • 第一個元素的長度為 5 ,值為 "apple" 。
  • 第二個元素的長度為 6 ,值為 "banana" 。
  • 第三個元素的長度為 3 ,值為 "cat" 。
  • 第四個元素的長度為 3 ,值為 "dog" 。

哈希表對象

如果 TYPE 的值為 REDIS_RDB_TYPE_HASH , 那么 value 保存的就是一個 REDIS_ENCODING_HT 編碼的集合對象, RDB 文件保存這種對象的結(jié)構(gòu)如圖 IMAGE_HT_HASH 所示:

  • hash_size 記錄了哈希表的大小, 也即是這個哈希表保存了多少鍵值對, 讀入程序可以通過這個大小知道自己應(yīng)該讀入多少個鍵值對。
  • 以 key_value_pair 開頭的部分代表哈希表中的鍵值對, 鍵值對的鍵和值都是字符串對象, 所以程序會以處理字符串對象的方式來保存和讀入鍵值對。

結(jié)構(gòu)中的每個鍵值對都以鍵緊挨著值的方式排列在一起, 如圖 IMAGE_KEY_VALUE_PAIR_OF_HT_HASH 所示。

因此, 從更詳細(xì)的角度看, 圖 IMAGE_HT_HASH 所展示的結(jié)構(gòu)可以進(jìn)一步修改為圖 IMAGE_DETIAL_HT_HASH 。

作為示例, 圖 IMAGE_EXAMPLE_OF_HT_HASH 展示了一個包含兩個鍵值對的哈希表。

在這個示例結(jié)構(gòu)中, 第一個數(shù)字 2 記錄了哈希表的鍵值對數(shù)量, 之后跟著的是兩個鍵值對:

  • 第一個鍵值對的鍵是長度為 1 的字符串 "a" , 值是長度為 5 的字符串 "apple" 。
  • 第二個鍵值對的鍵是長度為 1 的字符串 "b" , 值是長度為 6 的字符串 "banana" 。

有序集合對象

如果 TYPE 的值為 REDIS_RDB_TYPE_ZSET , 那么 value 保存的就是一個 REDIS_ENCODING_SKIPLIST 編碼的有序集合對象, RDB 文件保存這種對象的結(jié)構(gòu)如圖 IMAGE_SKIPLIST_ZSET 所示。

sorted_set_size 記錄了有序集合的大小, 也即是這個有序集合保存了多少元素, 讀入程序需要根據(jù)這個值來決定應(yīng)該讀入多少有序集合元素。

以 element 開頭的部分代表有序集合中的元素, 每個元素又分為成員(member)和分值(score)兩部分, 成員是一個字符串對象, 分值則是一個 double 類型的浮點數(shù), 程序在保存 RDB 文件時會先將分值轉(zhuǎn)換成字符串對象, 然后再用保存字符串對象的方法將分值保存起來。

有序集合中的每個元素都以成員緊挨著分值的方式排列, 如圖 IMAGE_MEMBER_AND_SCORE_OF_ZSET 所示。

因此, 從更詳細(xì)的角度看, 圖 IMAGE_SKIPLIST_ZSET 所展示的結(jié)構(gòu)可以進(jìn)一步修改為圖 IMAGE_DETIAL_SKIPLIST_ZSET 。

作為示例, 圖 IMAGE_EXAMPLE_OF_SKIPLIST_ZSET 展示了一個帶有兩個元素的有序集合。

在這個示例結(jié)構(gòu)中, 第一個數(shù)字 2 記錄了有序集合的元素數(shù)量, 之后跟著的是兩個有序集合元素:

  • 第一個元素的成員是長度為 2 的字符串 "pi" , 分值被轉(zhuǎn)換成字符串之后變成了長度為 4 的字符串 "3.14" 。
  • 第二個元素的成員是長度為 1 的字符串 "e" , 分值被轉(zhuǎn)換成字符串之后變成了長度為 3 的字符串 "2.7" 。

INTSET 編碼的集合

如果 TYPE 的值為 REDIS_RDB_TYPE_SET_INTSET , 那么 value 保存的就是一個整數(shù)集合對象, RDB 文件保存這種對象的方法是, 先將整數(shù)集合轉(zhuǎn)換為字符串對象, 然后將這個字符串對象保存到 RDB 文件里面。

如果程序在讀入 RDB 文件的過程中, 碰到由整數(shù)集合對象轉(zhuǎn)換成的字符串對象, 那么程序會根據(jù) TYPE 值的指示, 先讀入字符串對象, 再將這個字符串對象轉(zhuǎn)換成原來的整數(shù)集合對象。

ZIPLIST 編碼的列表、哈希表或者有序集合

如果 TYPE 的值為 REDIS_RDB_TYPE_LIST_ZIPLIST 、 REDIS_RDB_TYPE_HASH_ZIPLIST 或者 REDIS_RDB_TYPE_ZSET_ZIPLIST , 那么 value 保存的就是一個壓縮列表對象, RDB 文件保存這種對象的方法是:

  1. 將壓縮列表轉(zhuǎn)換成一個字符串對象。
  2. 將轉(zhuǎn)換所得的字符串對象保存到 RDB 文件。

如果程序在讀入 RDB 文件的過程中, 碰到由壓縮列表對象轉(zhuǎn)換成的字符串對象, 那么程序會根據(jù) TYPE 值的指示, 執(zhí)行以下操作:

  1. 讀入字符串對象,并將它轉(zhuǎn)換成原來的壓縮列表對象。
  2. 根據(jù) TYPE 的值,設(shè)置壓縮列表對象的類型: 如果 TYPE 的值為 REDIS_RDB_TYPE_LIST_ZIPLIST , 那么壓縮列表對象的類型為列表; 如果TYPE 的值為 REDIS_RDB_TYPE_HASH_ZIPLIST , 那么壓縮列表對象的類型為哈希表; 如果 TYPE 的值為 REDIS_RDB_TYPE_ZSET_ZIPLIST , 那么壓縮列表對象的類型為有序集合。

從步驟 2 可以看出, 由于 TYPE 的存在, 即使列表、哈希表和有序集合三種類型都使用壓縮列表來保存, RDB 讀入程序也總可以將讀入并轉(zhuǎn)換之后得出的壓縮列表設(shè)置成原來的類型。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號