redis源碼分析之RDB文件

     

RDB文件名和路徑                                       

  由配置redis.conf中

                dbfilename dump.rdb   // RDB文件名

                dir ./                                  // RDB文件所在的絕對路徑

RDB保存時的臨時文件名                              

       RDB保存時會臨時把數據存入到上述dir定義的目錄下的臨時文件“dump-pid.rdb”(pid爲當前運行RDB保存進程的進程pid),RDB保存成功後,會執行rename操作,實現原子操作

RDB文件格式                                              

一個 RDB 文件可以分爲以下幾個部分:

+-------+-------------+-----------+-----------------+-----+-----------+
| REDIS | RDB-VERSION | SELECT-DB | KEY-VALUE-PAIRS | EOF | CHECK-SUM |
+-------+-------------+-----------+-----------------+-----+-----------+

                      |<-------- DB-DATA ---------->|

以下的幾個小節將分別對這幾個部分的保存和讀入規則進行介紹。

REDIS

文件的最開頭保存着 REDIS 五個字符,標識着一個 RDB 文件的開始。

在讀入文件的時候,程序可以通過檢查一個文件的前五個字節,來快速地判斷該文件是否有可能是 RDB 文件。

RDB-VERSION

一個四字節長的以字符表示的整數,記錄了該文件所使用的 RDB 版本號。

目前的 RDB 文件版本爲 0006

因爲不同版本的 RDB 文件互不兼容,所以在讀入程序時,需要根據版本來選擇不同的讀入方式。

DB-DATA

這個部分在一個 RDB 文件中會出現任意多次,每個 DB-DATA 部分保存着服務器上一個非空數據庫的所有數據。

SELECT-DB

這域保存着跟在後面的鍵值對所屬的數據庫號碼。

在讀入 RDB 文件時,程序會根據這個域的值來切換數據庫,確保數據被還原到正確的數據庫上。

KEY-VALUE-PAIRS

因爲空的數據庫不會被保存到 RDB 文件,所以這個部分至少會包含一個鍵值對的數據。

每個鍵值對的數據使用以下結構來保存:

+----------------------+---------------+-----+-------+
| OPTIONAL-EXPIRE-TIME | TYPE-OF-VALUE | KEY | VALUE |
+----------------------+---------------+-----+-------+

OPTIONAL-EXPIRE-TIME 域是可選的,如果鍵沒有設置過期時間,那麼這個域就不會出現;反之,如果這個域出現的話,那麼它記錄着鍵的過期時間,在當前版本的 RDB 中,過期時間是一個以毫秒爲單位的 UNIX 時間戳。

KEY 域保存着鍵,格式和 REDIS_ENCODING_RAW 編碼的字符串對象一樣(見下文)。

TYPE-OF-VALUE 域記錄着 VALUE 域的值所使用的編碼,根據這個域的指示,程序會使用不同的方式來保存和讀取 VALUE 的值。

保存 VALUE 的詳細格式如下:

  • REDIS_ENCODING_INT 編碼的 REDIS_STRING 類型對象:

    如果值可以表示爲 8 位、 16 位或 32 位有符號整數,那麼直接以整數類型的形式來保存它們:

    +---------+
    | integer |
    +---------+
    

    比如說,整數 8 可以用 8 位序列 00001000 保存。

    當讀入這類值時,程序按指定的長度讀入字節數據,然後將數據轉換回整數類型。

    另一方面,如果值不能被表示爲最高 32 位的有符號整數,那麼說明這是一個 long long 類型的值,在 RDB 文件中,這種類型的值以字符序列的形式保存。

    一個字符序列由兩部分組成:

    +-----+---------+
    | LEN | CONTENT |
    +-----+---------+
    

    其中, CONTENT 域保存了字符內容,而 LEN 則保存了以字節爲單位的字符長度。

    當進行載入時,讀入器先讀入 LEN ,創建一個長度等於 LEN 的字符串對象,然後再從文件中讀取 LEN 字節數據,並將這些數據設置爲字符串對象的值。

  • REDIS_ENCODING_RAW 編碼的 REDIS_STRING 類型值有三種保存方式:

    1. 如果值可以表示爲 8 位、 16 位或 32 位長的有符號整數,那麼用整數類型的形式來保存它們。

    2. 如果字符串長度大於 20 ,並且服務器開啓了 LZF 壓縮功能 ,那麼對字符串進行壓縮,並保存壓縮之後的數據。

      經過 LZF 壓縮的字符串會被保存爲以下結構:

      +----------+----------------+--------------------+
      | LZF-FLAG | COMPRESSED-LEN | COMPRESSED-CONTENT |
      +----------+----------------+--------------------+
      

      LZF-FLAG 告知讀入器,後面跟着的是被 LZF 算法壓縮過的數據。

      COMPRESSED-CONTENT 是被壓縮後的數據, COMPRESSED-LEN 則是該數據的字節長度。

    3. 在其他情況下,程序直接以普通字節序列的方式來保存字符串。比如說,對於一個長度爲 20 字節的字符串,需要使用 20 字節的空間來保存它。

      這種字符串被保存爲以下結構:

      +-----+---------+
      | LEN | CONTENT |
      +-----+---------+
      

      LEN 爲字符串的字節長度, CONTENT 爲字符串。

    當進行載入時,讀入器先檢測字符串保存的方式,再根據不同的保存方式,用不同的方法取出內容,並將內容保存到新建的字符串對象當中。

  • REDIS_ENCODING_LINKEDLIST 編碼的 REDIS_LIST 類型值保存爲以下結構:

    +-----------+--------------+--------------+-----+--------------+
    | NODE-SIZE | NODE-VALUE-1 | NODE-VALUE-2 | ... | NODE-VALUE-N |
    +-----------+--------------+--------------+-----+--------------+
    

    其中 NODE-SIZE 保存鏈表節點數量,後面跟着 NODE-SIZE 個節點值。節點值的保存方式和字符串的保存方式一樣。

    當進行載入時,讀入器讀取節點的數量,創建一個新的鏈表,然後一直執行以下步驟,直到指定節點數量滿足爲止:

    1. 讀取字符串表示的節點值
    2. 將包含節點值的新節點添加到鏈表中
  • REDIS_ENCODING_HT 編碼的 REDIS_SET 類型值保存爲以下結構:

    +----------+-----------+-----------+-----+-----------+
    | SET-SIZE | ELEMENT-1 | ELEMENT-2 | ... | ELEMENT-N |
    +----------+-----------+-----------+-----+-----------+
    

    SET-SIZE 記錄了集合元素的數量,後面跟着多個元素值。元素值的保存方式和字符串的保存方式一樣。

    載入時,讀入器先讀入集合元素的數量 SET-SIZE ,再連續讀入 SET-SIZE 個字符串,並將這些字符串作爲新元素添加至新創建的集合。

  • REDIS_ENCODING_SKIPLIST 編碼的 REDIS_ZSET 類型值保存爲以下結構:

    +--------------+-------+---------+-------+---------+-----+-------+---------+
    | ELEMENT-SIZE | MEB-1 | SCORE-1 | MEB-2 | SCORE-2 | ... | MEB-N | SCORE-N |
    +--------------+-------+---------+-------+---------+-----+-------+---------+
    

    其中 ELEMENT-SIZE 爲有序集元素的數量, MEB-i 爲第 i 個有序集元素的成員, SCORE-i 爲第 i 個有序集元素的分值。

    當進行載入時,讀入器讀取有序集元素數量,創建一個新的有序集,然後一直執行以下步驟,直到指定元素數量滿足爲止:

    1. 讀入字符串形式保存的成員 member
    2. 讀入字符串形式保存的分值 score ,並將它轉換爲浮點數
    3. 添加 member 爲成員、 score 爲分值的新元素到有序集
  • REDIS_ENCODING_HT 編碼的 REDIS_HASH 類型值保存爲以下結構:

    +-----------+-------+---------+-------+---------+-----+-------+---------+
    | HASH-SIZE | KEY-1 | VALUE-1 | KEY-2 | VALUE-2 | ... | KEY-N | VALUE-N |
    +-----------+-------+---------+-------+---------+-----+-------+---------+
    

    HASH-SIZE 是哈希表包含的鍵值對的數量, KEY-i VALUE-i 分別是哈希表的鍵和值。

    載入時,程序先創建一個新的哈希表,然後讀入 HASH-SIZE ,再執行以下步驟 HASH-SIZE 次:

    1. 讀入一個字符串
    2. 再讀入另一個字符串
    3. 將第一個讀入的字符串作爲鍵,第二個讀入的字符串作爲值,插入到新建立的哈希中。
  • REDIS_LIST 類型、 REDIS_HASH 類型和 REDIS_ZSET 類型都使用了 REDIS_ENCODING_ZIPLIST 編碼, ziplist 在 RDB 中的保存方式如下:

    +-----+---------+
    | LEN | ZIPLIST |
    +-----+---------+
    

    載入時,讀入器先讀入 ziplist 的字節長,再根據該字節長讀入數據,最後將數據還原成一個 ziplist

  • REDIS_ENCODING_INTSET 編碼的 REDIS_SET 類型值保存爲以下結構:

    +-----+--------+
    | LEN | INTSET |
    +-----+--------+
    

    載入時,讀入器先讀入 intset 的字節長度,再根據長度讀入數據,最後將數據還原成 intset

EOF

標誌着數據庫內容的結尾(不是文件的結尾),值爲 rdb.h/EDIS_RDB_OPCODE_EOF255)。

CHECK-SUM

RDB 文件所有內容的校驗和,一個 uint_64t 類型值。

REDIS 在寫入 RDB 文件時將校驗和保存在 RDB 文件的末尾,當讀取時,根據它的值對內容進行校驗。

如果這個域的值爲 0 ,那麼表示 Redis 關閉了校驗和功能。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章