Redis系列(二):存儲結構

Redis存儲結構

1 簡介

Redis系列(一):Redis數據結構 可以瞭解到,Redis爲用戶提供了多種數據結構及相應API,用戶可隨心使用,本文將具體介紹這些數據結構的具體實現。

2 Redis存儲結構

Redis主要實現了6中存儲結構,分別爲:整數集合;字典、字符串;鏈表、壓縮列表、跳躍表。

2.1 SDS

SDS是Simple Dynamic String 的首字母縮寫,是對C語言中的字符串的封裝,其主要數據結構定義如下:

struct sdshdr {
    // 表示已分配使用的字節數
    // buf數組已使用的長度
    int len;
    // 表示SDS未使用的字節數;
    int free;
    // 字節數組保存二進制數據
    // 遵循C語言規則,以'\0'結尾,但不計算在len屬性
    char buf[];
};

SDS結構示意圖

Redis系列(一):Redis數據結構 已經講過,Redis所有的鍵都是字符串類型,也就是說所有的鍵都是SDS對象,舉例來說:

set msg “hello world”
該命令創建了一個鍵值對,且鍵和值都是字符串類型,鍵是一個保存着msg的SDS,值是保存着 hello world的SDS;

SDS是對在C字符串的基礎上進行的封裝,具有以下優勢:

  • 常數複雜度獲取字符串長度:C語言獲取字符串長度需要進行一次便利,SDS只需要通過獲取len屬性即可達到目的;
  • 減少修改字符串時帶來的內存重分配:C字符串拼接或截斷操作都需要重新分配內存空間,而SDS在分配內存空間時,預留了free長度的空閒空間,能夠有效減少內存衝分配次數;
  • 二進制安全:C字符串只能保存文本數據,而SDS能保存包括圖片、音頻、視頻等二進制數據,其原因是SDS是通過len屬性而不是’\0’來判斷字符串的結尾。
  • 杜絕緩衝區溢出:C字符串拼接需要手動分配內存空間,以容納拼接後的字符串,如果忘記該操作就會導致緩衝區溢出,SDS已經封裝好了字符串拼接操作,可完全避免緩衝區溢出問題。

    2.2 鏈表

    鏈表是一種常用的數據結構,其基本特性大家都應該很瞭解,如高效的節點重排能力、順序性節點訪問、靈活的節點增刪等。Redis使用的C語言並沒有內置這種數據結構,因此Redis自己實現了鏈表結構。
    鏈表節點listNode主要包含三個屬性:前置節點 *prev、後置節點 *next及節點值 *value,並且通過list結構來持有鏈表:

/**
 * 鏈表節點數據結構
 **/
typedef struct listNode{
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

/**
 * 通過list持有鏈表
 **/
typedef struct list{
    // 頭節點
    listNode *head;
    // 尾節點
    listNode *tail;
    // 鏈表節點數量
    unsigned int len;
    // 節點複製函數
    void *(*dup)(void *ptr);
    // 節點釋放函數
    void *(*free)(void *ptr);
    // 節點值對比函數
    void *(*match)(void *ptr);
} list;

Redis鏈表具有以下特徵:

  • 雙端:list結構持有head和tail指針,獲取表頭或表尾節點時間複雜度爲O(1);
  • 雙向:鏈表每個節點都帶有prev和next指針,獲取前置節點或後置節點時間複雜度爲O(1);
  • 無環:頭節點的prev和尾節點的next都爲NULL;
  • 長度計數器:list通過len屬性記錄鏈表節點數量;

    2.3 整數集合

    整數集合是集合鍵的底層實現之一,當一個集合只包含整數且數量不多時,Redis就會使用整數集合intset作爲底層實現。
    intset可以保存int16_t, int32_t, int64_t的整數值,並且可以保證不重複。

typedef struct intset {
    // 編碼方式
    uint32_t encoding;
    // 集合包含的元素數量
    uint32_t length;
    // 升序保存着所有元素
    int8_t contents[];
} intset;

contents屬性聲明爲int8_t類型,但實際上元素的類型取決於encoding屬性的值:

  • 如果encoding爲INTSET_ENC_INT16,那麼contents所有元素都是int16_t類型整數值;
  • 如果encoding爲INTSET_ENC_INT32,那麼contents所有元素都是int32_t類型整數值;
  • 如果encoding爲INTSET_ENC_INT64,那麼contents所有元素都是int6_4t類型整數值;

    升級: 當將一個新元素添加到整數集合,且其長度比集合中任何元素類型都長時,整數集合需要先進行升級,升級分爲三步:
    1)根據新元素類型,分配內存空間,包括已有元素和新元素;
    2)將已有元素轉換成新元素類型,並放置到新的內存空間;
    3)將新元素添加到內存空間;
    整個升級過程保證所有元素升序排序。對於具體的內存分配過程,請大家參閱《Redis設計與實現》。
    需要說明的是 整數集合不支持降級,因此一旦對數組進行了升級,編碼就會一直保持升級後的狀態。

2.4

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