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 主要作為記憶體資料庫,但支援持久化到磁碟。
兩種持久化策略#
清除結構,只存資料(Redis 採用)
- 優點:儲存格式簡單
- 缺點:恢復時需重建結構(如重新計算雜湊值)
保留結構格式
- 優點:恢復快速
- 缺點:儲存格式複雜
Redis 重啟並不頻繁,選擇第一種方案在大多數場景下是可接受的。
設計啟示#
- 按資料量選擇結構:小資料用省空間的,大資料用高效能的
- 漸進式操作:避免阻塞主執行緒
- 簡單優先:跳表比紅黑樹更適合實際工程