1. 程式人生 > 實用技巧 >Redis底層資料結構

Redis底層資料結構

  • RedisObject

    Redis 是 key-value 儲存系統,其中key型別一般為字串,而 value 型別則為 redis 物件(RedisObject)。Redis 物件可以繫結各種型別的資料,譬如 string、list 和set。因此他能很好的將屬性和資料分離開。

    typedef struct redisObject {
    // 物件的型別
    unsigned type:4;
    // 編碼的方式
    unsigned encoding:4;
    / 物件最後一次被訪問的時間
    unsigned lru:22;
    // 引用計數
    int refcount;
    // 資料指標
    void *ptr;
    } robj;

    為什麼要有一個RedisObject?而不直接使用五大型別物件?

    1. 通過不同型別的物件,Redis 可以在執行命令之前,根據物件的型別來判斷一個物件是否可以執行給定的命令。

    2. 我們可以針對不同的使用場景,為物件設定不同的實現,從而優化記憶體或查詢速度。

    type 物件的型別

    型別常量物件的名稱
    REDIS_STRING 字串物件
    REDIS_LIST 列表物件
    REDIS_HASH 雜湊物件
    REDIS_SET 集合物件
    REDIS_ZSET 有序集合物件

    ptr 指標

    指向實際儲存的物件的指標

    encoding

    encoding 表示 ptr 指向的具體資料結構,即這個物件使用了什麼資料結構作為底層實現。

    編碼常量編碼所對應的底層資料結構
    REDIS_ENCODING_INT long型別的整數
    REDIS_ENCODING_EMBSTR enbstr編碼的簡單動態字串
    REDIS_ENCODING_RAW 簡單動態字串
    REDIS_ENCODING_HT 字典
    REDIS_ENCODING_LINKEDLIST 雙向連結串列
    REDIS_ENCODING_ZIPLIST 壓縮列表
    REDIS_ENCODING_INTLIST 整數集合
    REDIS_ENCODING_SKIPLIST 跳錶

    每種型別的物件都至少使用了兩種不同的編碼,物件和編碼的對應關係如下

    refcount

    引用計數器,當該值為0時,表示該物件已經不再被引用了,可以釋放進行記憶體回收。

    lru

    最近一次訪問時間

  • String

    String型別的encoding方式有三種,分別是int、raw、enbstr。

    1. 如果儲存的資料是整數,且該整數是8個位元組能表示的資料,那麼該整數就可以直接儲存在ptr中。這種方式節約記憶體,且可以快速讀寫索引。其記憶體結構如下:

    2. 如果儲存的資料是字串,且該字串長度小於等於32位元組,那麼字串將使用enbstr的編碼方式來儲存資料。因為這種方式在申請記憶體空間時是一次申請的,所以需要分配記憶體空間一次,釋放也只需要一次,並且所有資料儲存在一塊連續的記憶體空間內,可以減少記憶體碎片的產生。

    3. 如果儲存的資料是字串,且該字串的長度大於32位元組,那麼字串將會使用raw的編碼方式來儲存資料。

  • Hash

    雜湊型別的底層實現結構有倆種,壓縮列表以及hash表;

    列表物件儲存的所有字串元素的長度都小於64位元組,列表物件儲存的元素數量小於512個使用壓縮列表,否則自動轉成hash表;

  • List

    列表物件儲存的所有字串元素的長度都小於64位元組,列表物件儲存的元素數量小於512個使用壓縮列表,否則自動轉成linklist表;

    列表使用的是雙向連結串列的實現形式實現的;

    列表使用ziplist實現

    列表使用linkedlist實現

    3.2版本後引入了quicklist,快速列表,quicklist是使用ziplist和linkedlist結合實現的,它把linkedlist分成多段,每段使用ziplist實現保持資料的緊湊,多個ziplist使用雙向指標連線成連結串列;

    列表使用quicklist實現

  • Set

    集合分別使用整數陣列和雜湊表實現;

    當集合長度小於512,且儲存的元素都是整數時,使用整數陣列,否則使用雜湊表儲存;

    整數陣列優點是節省空間,缺點是索引慢,所以在資料量較少時使用這種方式實現;

  • ZSet

    有序集合分別使用壓縮列表和zset實現;

    當集合長度小於128時,且所有元素的長度小於64時使用壓縮列表實現,否則使用跳錶;

    zset是使用跳錶和字典實現的;其資料結構如下圖:

    跳錶是指通過維護多級索引,來優化查詢時的速度。

    跳錶結構

    資料結構時間複雜度

    名稱時間複雜度
    雜湊表 O(1) 查詢時間穩定良好
    跳錶 O(logN) 資料量越大,時間複雜度逐漸降低
    雙向連結串列 O(N) 根據資料量線性增大
    壓縮列表 O(N)根據資料量線性增大
    整數陣列 O(N)根據資料量線性增大

    各結構優點

    名稱時間複雜度
    雜湊表 查詢快
    跳錶 有序,資料量大時查詢快
    雙向連結串列 頭尾節點訪問快,適合按序訪問
    壓縮列表 節省空間記憶體,小資料量情況使用
    整數陣列 節省空間記憶體,小資料量情況使用,無序