Redis設計與實現閱讀總結(一)資料結構和物件

不知發表於2019-03-20

大家有什麼建議歡迎在下方評論提出,多多討論

大家可能發現這個部分寫的很粗略,因為redis本身設計的就是很簡單的,這些資料結構基本沒有太多好寫的,主要是寫 了後做一個總結

1. SDS(簡單動態字串)

每個 sds.h/sdshdr 結構表示一個 SDS 值:

struct sdshdr {

    // 記錄 buf 陣列中已使用位元組的數量
    // 等於 SDS 所儲存字串的長度
    int len;

    // 記錄 buf 陣列中未使用位元組的數量
    int free;

    // 位元組陣列,用於儲存字串
    char buf[];

};
複製程式碼

Redis設計與實現閱讀總結(一)資料結構和物件

為什麼不使用c語言簡單字串

原因有如下

  • 獲取長度複雜度為O(1)
  • 避免緩衝區溢位

優化策略

redis非常注重效能,於是優化是非常必要的。

減少記憶體重分配

  1. 空間預分配

記憶體的分配設計到複雜演算法或可能的系統呼叫,比較耗時

於是,每次再字串增長操作時,不僅僅會擴容到剛好所需的空間,而是會通過一定策略額外分配空間

  1. 惰性空間釋放

字串的縮短操作,不會進行記憶體重分配,而是會通過free記錄起來,為未來可能存在的增長提供優化

二進位制安全

不使用\0辨認結尾,而是通過len

並且在SDS中\0被保留,可以相容部分c字串函式

2. 連結串列

redis是一個設計追求簡單的資料庫,連結串列這種簡單實用的資料結構在很多地方都得到了應用,(事實上應該說是雙向連結串列。)

譬如 列表(List),慢查詢,監視器,訂閱與釋出等。包括客戶端狀態也是用連結串列在伺服器中進行儲存的。

結構

typedef struct listNode {

    // 前置節點
    struct listNode *prev;

    // 後置節點
    struct listNode *next;

    // 節點的值
    void *value;

} listNode;
複製程式碼

Redis設計與實現閱讀總結(一)資料結構和物件

Redis設計與實現閱讀總結(一)資料結構和物件

3. 字典

字典是使用雜湊表作為底層實現。

Redis設計與實現閱讀總結(一)資料結構和物件

一圖勝千言

看的出來,解決鍵衝突(collision)的辦法是通過每個雜湊表節點都是一個連結串列。(鏈地址法)

  • 雜湊演算法
  • 鏈地址法

以上兩者細節就不解釋了

rehash擴容

Redis設計與實現閱讀總結(一)資料結構和物件

要擴多少內容,開多大空間的新dictEntry,然後把之前的dictEntry轉到新的。

當然,一次性轉會有效能問題,所以

漸進式rehash

相當於是搭便車

每次對字典進行更新新增查詢刪除操作時,會順帶的轉移舊的dictEntry的一個index連結串列到新的dictEntry

詳細執行過程不予介紹

4. 跳躍表(跳錶)

跳躍表(skiplist)是一種有序資料結構, 它通過在每個節點中維持多個指向其他節點的指標, 從而達到快速訪問節點的目的。

跳躍表支援平均 O(\log N) 最壞 O(N) 複雜度的節點查詢, 還可以通過順序性操作來批量處理節點。

在大部分情況下, 跳躍表的效率可以和平衡樹相媲美, 並且因為跳躍表的實現比平衡樹要來得更為簡單, 所以有不少程式都使用跳躍表來代替平衡樹。

Redis 使用跳躍表作為有序集合鍵的底層實現之一: 如果一個有序集合包含的元素數量比較多, 又或者有序集合中元素的成員(member)是比較長的字串時, Redis 就會使用跳躍表來作為有序集合鍵的底層實現

Redis設計與實現閱讀總結(一)資料結構和物件

從上圖可以看書,跳錶其實就是一種分治思想的,利用空間換取時間的一種演算法 在看了上圖之後,這個時候再看下圖redis的跳錶。就應該容易理解多了

Redis設計與實現閱讀總結(一)資料結構和物件

整數集合

整數集合就比較簡單了,重要的一個點主要是升級

redis的整數集合型別其實是會隨著存入數字而變化的,當數都比較小的時候,型別可能就是int8_t,int16_t,但是一旦加入一個大數,集合會升級型別為相應型別,此時該集合中所有數都會被更新為該型別

另外注意

整數集合的陣列不會出現降級

Redis設計與實現閱讀總結(一)資料結構和物件

壓縮列表

物件

相關文章