Redis 資料結構剖析#

Redis 是一個高效能的記憶體資料庫,其核心就是常用資料結構的封裝。

五大資料類型與底層實作#

flowchart LR
    subgraph 資料類型
        S[String]
        L[List]
        H[Hash]
        ST[Set]
        SS[Sorted Set]
    end

    subgraph 小資料量
        S1[字串]
        L1[壓縮列表]
        H1[壓縮列表]
        ST1[有序陣列]
        SS1[壓縮列表]
    end

    subgraph 大資料量
        S2[字串]
        L2[雙向鏈結串列]
        H2[散列表]
        ST2[散列表]
        SS2[跳表]
    end

    S --> S1
    S1 -.->|資料增長| S2
    L --> L1
    L1 -.->|資料增長| L2
    H --> H1
    H1 -.->|資料增長| H2
    ST --> ST1
    ST1 -.->|資料增長| ST2
    SS --> SS1
    SS1 -.->|資料增長| SS2

    style L1 fill:#e3f2fd
    style H1 fill:#e3f2fd
    style SS1 fill:#e3f2fd
    style L2 fill:#fff3e0
    style H2 fill:#fff3e0
    style SS2 fill:#fff3e0
資料類型小資料量實作大資料量實作
String字串字串
List壓縮列表雙向迴圈鏈結串列
Hash壓縮列表散列表
Set有序陣列散列表
Sorted Set壓縮列表跳表

Redis 在資料量小時使用更節省記憶體的結構,資料量大時切換到更高效的結構。這是時間與空間權衡的典型案例。

壓縮列表 (Ziplist)#

一種類似陣列的連續記憶體結構,但允許儲存不同大小的元素。

結構#

[總長度][尾偏移][元素數][元素1長度][元素1][元素2長度][元素2]...[結束標記]

特點#

  • 優點:節省記憶體,快取友好
  • 缺點:不支援隨機存取,需連續記憶體

使用條件#

類型條件
List單個元素 < 64 位元組 且 元素數 < 512
Hash鍵值大小 < 64 位元組 且 鍵值對數 < 512
Sorted Set元素大小 < 64 位元組 且 元素數 < 128

雙向鏈結串列#

當 List 資料量超過閾值時切換使用。

Redis 鏈結串列實作
typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

typedef struct list {
    listNode *head;
    listNode *tail;
    unsigned long len;
    // ...其他欄位
} list;

額外的 list 結構體記錄頭尾指標和長度,使操作更方便。

散列表 (Hash)#

  • 雜湊函式:MurmurHash2(速度快、隨機性好)
  • 衝突解決:鏈結法
  • 擴縮容:漸進式 rehash

擴縮容觸發條件#

操作條件
擴容裝載因子 > 1
縮容裝載因子 < 0.1

漸進式 Rehash:將資料搬移分批進行,避免一次性搬移導致服務停頓。

跳表 (Skip List)#

用於 Sorted Set 的大資料量實作。

為什麼用跳表而不是紅黑樹?#

特性跳表紅黑樹
實作難度簡單複雜
範圍查詢高效(順序走訪)需中序走訪
調試維護容易困難

資料持久化#

Redis 主要作為記憶體資料庫,但支援持久化到磁碟。

兩種持久化策略#

  1. 清除結構,只存資料(Redis 採用)

    • 優點:儲存格式簡單
    • 缺點:恢復時需重建結構(如重新計算雜湊值)
  2. 保留結構格式

    • 優點:恢復快速
    • 缺點:儲存格式複雜

Redis 重啟並不頻繁,選擇第一種方案在大多數場景下是可接受的。

設計啟示#

  1. 按資料量選擇結構:小資料用省空間的,大資料用高效能的
  2. 漸進式操作:避免阻塞主執行緒
  3. 簡單優先:跳表比紅黑樹更適合實際工程