目錄
一、RedisDB結構
1、RedisDB在Redis實例中的位置
2、RedisDB結構與核心組件
二、RedisObject結構
1、核心數據結構
1.1 簡單動態字符串 (Simple Dynamic String - SDS)
1.2?字典 (Dict / Hash Table)
1.3?雙端鏈表 (Linked List)
1.4 跳躍表 (Skip List)
1.5?壓縮列表 (ZipList) -?Redis 7.0 起被 Listpack 取代
1.6?緊湊列表 (Listpack) -?*Redis 5.0 引入,7.0 成為小規模列表/哈希/有序集合的默認*
1.7?整數集合 (IntSet)
1.8?快速列表 (QuickList) -?Redis 3.2 引入
2、對象系統(RedisObject)
2.1 結構信息概覽
2.2 核心作用
2.3 類型與編碼映射
2.3.1?字符串 (String)
2.3.2?列表 (List)
2.3.3?哈希 (Hash)
2.3.4?集合 (Set)
2.3.5?有序集合 (Sorted Set)
一、RedisDB結構
Redis 的數據庫由?redisDb
?結構體表示,它是 Redis 存儲鍵值對、管理過期時間、實現阻塞操作、事務以及維護數據庫狀態的核心數據結構。每個 Redis 實例默認有 16 個獨立的數據庫(編號 0-15),可通過?SELECT
?命令切換。
SELECT <db_index> # <db_index> 為目標數據庫編號(整數)
1、RedisDB在Redis實例中的位置
一個 Redis 服務器實例 (redisServer
?結構) 包含一個?redisDb
?數組:
struct redisServer {...redisDb *db; /* 指向一個 dbnum 大小的 redisDb 數組 */int dbnum; /* 數據庫數量 (默認 16) */...
};
客戶端狀態 (client
?結構) 中有一個指針指向其當前選擇的數據庫:
typedef struct client {...redisDb *db; /* 指向當前客戶端選擇的數據庫 */...
} client;
當客戶端執行?SELECT 2
?時,其?db
?指針就被設置為指向?server.db[2]
。
2、RedisDB結構與核心組件
Redis結構:
typedef struct redisDb {dict *dict; /* 核心:鍵空間(Keyspace),存儲所有鍵值對 */dict *expires; /* 過期字典:存儲鍵的過期時間(毫秒時間戳) */dict *blocking_keys; /* 阻塞鍵:記錄因 B[L/R]POP 等命令阻塞的鍵及等待的客戶端 */dict *ready_keys; /* 就緒鍵:記錄有數據 PUSH 進來、可解除客戶端阻塞的鍵 */dict *watched_keys; /* 被 WATCH 監視的鍵:用于事務 CAS 樂觀鎖 */int id; /* 數據庫 ID (0-15) */long long avg_ttl; /* 平均 TTL(統計用) */unsigned long expires_cursor; /* 過期鍵掃描游標(用于周期性刪除) */list *defrag_later; /* 后續嘗試內存碎片整理的鍵列表 */
} redisDb;
核心組件:
1>?dict *dict
?(鍵空間 - Keyspace)
-
作用:?存儲該數據庫中所有鍵值對的核心字典。
-
鍵 (Key):?Redis 字符串對象(內部是 SDS)。
-
值 (Value):?指向?
redisObject
?結構的指針。redisObject
?封裝了實際的數據類型(String, List, Hash, Set, ZSet)及其底層實現(SDS, QuickList, Dict, IntSet, SkipList 等)。 -
操作:?所有對鍵的增刪改查(
SET
,?GET
,?DEL
,?EXISTS
,?KEYS
?等)都直接作用于這個字典。
2>?dict *expires
?(過期字典 - Expires Dictionary)
-
作用:?存儲設置了過期時間 (TTL)?的鍵及其過期時間戳(毫秒精度的 UNIX 時間戳)。
-
鍵 (Key):?與?
dict
?中的鍵共享同一個 SDS 對象(指針相同,節省內存)。 -
值 (Value):?
long long
?類型的整數,表示鍵的絕對過期時間戳(pexpireat
?設置的時間點)。 -
關鍵機制:
-
惰性刪除 (Lazy Expiration):?當訪問一個鍵時(
GET
,?HGET
,?LRANGE
?等命令),Redis 會先檢查?expires
?字典。如果該鍵存在且當前時間已超過其存儲的時間戳,則立即刪除該鍵(從?dict
?和?expires
?中移除),然后返回?nil
?或錯誤。這保證了訪問到的鍵總是未過期的。 -
定期刪除 (Active Expiration):?Redis 的事件循環 (
serverCron
?函數) 會周期性(默認每秒 10 次,可配置?hz
)地主動掃描?expires
?字典:-
每次隨機抽取一定數量(默認 20 個)的過期鍵。
-
刪除其中已過期的鍵。
-
如果過期鍵比例超過 25%,則重復此過程。
-
使用?
expires_cursor
?記錄掃描位置,確保所有鍵都能被掃描到。
-
-
過期策略:?Redis 結合了惰性刪除(確保訪問準確性)和定期刪除(回收內存)兩種策略。
-
3>?dict *blocking_keys
?(阻塞鍵字典)
-
作用:?管理因執行?阻塞式列表彈出命令(如?
BLPOP
,?BRPOP
,?BRPOPLPUSH
)而等待數據的客戶端。 -
鍵 (Key):?被阻塞客戶端等待的列表鍵名(SDS)。
-
值 (Value):?一個指向鏈表的指針。該鏈表中存放了所有因等待這個鍵的數據而被阻塞的?
client
?結構(客戶端狀態)。 -
原理:?當客戶端執行?
BLPOP key1 key2 ... timeout
?時,如果所有指定列表都為空,客戶端會被阻塞。Redis 會將該客戶端添加到?blocking_keys
?中每個指定?key
?對應的阻塞客戶端鏈表中,并設置超時計時器。
4>?dict *ready_keys
?(就緒鍵字典)
-
作用:?作為?
blocking_keys
?的輔助結構,用于高效地處理阻塞解除。 -
鍵 (Key):?一個列表鍵名(SDS)。
-
值 (Value):?通常為?
NULL
(不重要),存在即表示該鍵有數據到達。 -
工作流程:
-
當有客戶端向一個空列表執行?
LPUSH
,?RPUSH
?等命令添加數據時,該列表鍵會被標記為就緒(添加到?ready_keys
?字典)。 -
在 Redis 的事件循環中(
beforeSleep
?函數),會檢查?ready_keys
?字典。 -
對于其中每個就緒鍵,Redis 會查找?
blocking_keys
?中該鍵對應的阻塞客戶端鏈表。 -
從鏈表中取出一個(或多個,取決于命令)客戶端,向其返回新添加的數據,并解除其阻塞狀態。
-
-
優點:?避免了在每次?
PUSH
?命令執行時直接遍歷阻塞客戶端鏈表帶來的性能開銷,將解除阻塞的操作集中處理。
5>?dict *watched_keys
?(監視鍵字典)
-
作用:?實現?
WATCH
?命令,為 Redis 事務提供樂觀鎖 (CAS - Check And Set)?機制。 -
鍵 (Key):?被客戶端?
WATCH
?的鍵名(SDS)。 -
值 (Value):?一個指向鏈表的指針。該鏈表中存放了所有?
WATCH
?了這個鍵的?client
?結構(客戶端狀態)。 -
事務流程:
-
客戶端使用?
WATCH key1 key2 ...
?監視一個或多個鍵。 -
客戶端開啟事務 (
MULTI
) 并發送命令隊列 (SET
,?INCR
?等)。 -
客戶端提交事務 (
EXEC
)。 -
執行?
EXEC
?前,Redis 檢查:-
遍歷客戶端?
WATCH
?的所有鍵。 -
檢查這些鍵在?
watched_keys
?中的鏈表是否存在(即鍵是否被修改?)。 -
檢查鍵自?
WATCH
?后是否被其他客戶端修改過(通過?redisObject
?的?lru
?字段或專門的?dirty
?標志)。
-
-
如果至少有一個被?
WATCH
?的鍵被修改過,服務器拒絕執行事務隊列 (EXEC
?返回?nil
),客戶端需要重試。 -
如果沒有被修改,則執行事務隊列中的所有命令。
-
-
鍵修改觸發:?任何成功修改鍵值的命令(
SET
,?INCR
,?LPUSH
,?DEL
?等)在執行后,會遍歷?watched_keys
?找到該鍵對應的鏈表,將其中所有客戶端的?REDIS_DIRTY_CAS
?標志置位,表示該客戶端監視的鍵已被改動,其事務將在?EXEC
?時失敗。
6>?int id
?(數據庫 ID)
-
標識該數據庫的編號,范圍是?
0
?到?server.dbnum - 1
(默認?0
?到?15
)。 -
客戶端通過?
SELECT id
?命令在不同數據庫間切換。
7>?long long avg_ttl
?(平均 TTL)
-
數據庫所有設置了 TTL 的鍵的平均剩余生存時間(毫秒)。這是一個統計值,并非實時精確計算,主要用于?
INFO
?命令輸出,幫助管理員了解數據庫過期鍵的大致情況。
8>?unsigned long expires_cursor
?(過期鍵掃描游標)
-
用于實現定期刪除策略中的漸進式掃描。記錄當前掃描?
expires
?字典的桶索引 (bucket index),確保每次?serverCron
?調用時掃描不同的部分,避免集中掃描導致延遲。
9>?list *defrag_later
?(后續碎片整理列表)
-
Redis 的內存碎片整理 (
MEMORY PURGE
?/?CONFIG SET activedefrag ...
) 可能無法一次性完成所有工作。 -
此列表保存了需要稍后進行碎片整理嘗試的鍵(指向?
dict
?中鍵的指針)。 -
碎片整理過程會逐步遍歷這個列表,嘗試對鍵指向的?
redisObject
?及其底層數據結構(如包含大量小元素的 Hash、List、ZSet)進行內存重排,減少碎片。
二、RedisObject結構
1、核心數據結構
1.1 簡單動態字符串 (Simple Dynamic String - SDS)
用途:?存儲字符串值、整型數據、鍵名、緩沖區等。
設計目標:?解決 C 語言原生字符串的缺陷,提高安全性和效率。
關鍵結構 (簡化):
struct sdshdr {int len; // 已使用字節長度 (字符串實際長度,不含'\0')int alloc; // 分配的總字節長度 (不包括 header 和 null terminator)char flags; // SDS 類型標識 (sdshdr5, sdshdr8, sdshdr16, sdshdr32, sdshdr64)char buf[]; // 柔性數組,存放實際字符串 + '\0'
};
優勢:
-
O(1) 復雜度獲取長度:?直接訪問?
len
?字段。C字符串是O(n)。 -
杜絕緩沖區溢出:?修改前檢查?
alloc
,空間不足自動擴容。 -
減少內存重分配:?空間預分配 (
alloc = len + newlen
) 和惰性空間釋放策略優化性能。 -
二進制安全:?可以存儲包含?
'\0'
?的任意二進制數據,靠?len
?判斷結束。由于二進制數據包括空字符串\0,C沒有辦法存取二進制數據。 -
兼容 C 字符串:?
buf
?末尾保留?'\0'
,可直接使用部分 C 字符串函數。
1.2?字典 (Dict / Hash Table)
用途:?存儲所有鍵值對、哈希類型數據、Set 類型數據(當元素為字符串且非整數時)等。Redis整個數據庫是用字典來存儲的。
核心結構:
-
?字典 (
dict
):
typedef struct dict {dictType *type; // 該字典對應的特定操作函數 (hash, keyDup, keyCompare, ...)void *privdata; // 上述類型函數對應的可選參數dictht ht[2]; // 兩張哈希表,存儲鍵值對數據,ht[0]為原生哈希表,ht[1]為 rehash 哈希表long rehashidx; // rehash標識 當等于-1時表示沒有在rehash,否則表示正在進行rehash操作,存儲的值表示hash表 ht[0]的rehash進行到哪個索引值(數組下標)int iterators; // 當前運行的迭代器數量
} dict;// 包含對該字典操作的函數指針
typedef struct dictType {// 計算哈希值的函數unsigned int (*hashFunction)(const void *key);// 復制鍵的函數void *(*keyDup)(void *privdata, const void *key);// 復制值的函數void *(*valDup)(void *privdata, const void *obj);// 比較鍵的函數int (*keyCompare)(void *privdata, const void *key1, const void *key2);// 銷毀鍵的函數void (*keyDestructor)(void *privdata, void *key);// 銷毀值的函數void (*valDestructor)(void *privdata, void *obj);
} dictType;
-
哈希表 (
dictht
):
typedef struct dictht {dictEntry **table; // 哈希桶數組指針(哈希表數組)unsigned long size; // 哈希表數組大小 (桶的數量,總是 2^n),初始容量為4,隨著k-v增加,新擴容量為當前量的一倍(4,8,16,32)unsigned long sizemask; // 用于映射位置的掩碼,值永遠等于 (size - 1),索引值=Hash值&掩碼值unsigned long used; // 已有節點數量,包含next單鏈表的數據
} dictht;
-
哈希表節點 (
dictEntry
):
typedef struct dictEntry {void *key; // 鍵union { // 值v的類型可以是以下4種類型void *val;uint64_t u64;int64_t s64;double d;} v; struct dictEntry *next; // 指向下一個節點,形成鏈表 (解決哈希沖突)
} dictEntry;
關鍵機制:
-
哈希沖突解決:?鏈地址法。相同桶內的節點用鏈表連接。
-
Rehash:
-
觸發條件:?
-
負載因子超標:
-
當?
ht[0].used / ht[0].size > dict_force_resize_ratio
(默認?5)時強制擴容 -
或?
used / size > 1
?且允許 rehash(無迭代器運行)時建議擴容
-
-
BGSAVE/BGREWRITEAOF 優化:若正在進行子進程持久化操作,負載因子閾值提升至?5(避免父進程 COW 內存復制)
-
-
漸進式rehash過程:?漸進式 rehash。分配?
ht[1]
,將?rehashidx
?從 0 開始遞增,每次增刪改查操作時,順帶將?ht[0]
?中?rehashidx
?桶的所有鍵值對遷移到?ht[1]
。完成后?ht[1]
?變為?ht[0]
,重置?ht[1]
?和?rehashidx
。-
步驟1:初始化rehash
-
分配
ht[1]
?空間:新容量 = 第一個大于等于?ht[0].used × 2
?的?2^n(如 6 → 8,10 → 16) -
設置?
rehashidx = 0
(標記 rehash 開始)
-
-
步驟2:分布遷移數據(每次操作遷移一個桶鏈/鏈表,分散到多次操作中完)
-
寫操作觸發遷移:新增加的數據在新的hash表h[1]
-
定時任務遷移:將老數據重新計算索引值后遷移到h[1]。
-
-
步驟3:完成遷移
-
當?
ht[0].used == 0
?時:-
釋放?
ht[0].table
-
將?
ht[1]
?賦值給?ht[0]
-
重置?
ht[1]
?為空表 -
設置?
rehashidx = -1
(標記 rehash 結束)
-
-
-
-
優點:?避免一次性遷移大量數據導致服務停頓。
擴容方式 平均延遲 內存峰值 適用場景 傳統一次性 rehash 高 1x 小數據集 Redis 漸進式 rehash 低 2x 生產環境大數據實時服務 -
縮容機制:當?
used < size/10
?時觸發縮容(避免內存浪費);流程與擴容相同 -
并發安全策略:
-
查找:同時在 ht[0] 和 ht[1] 中搜索
-
插入:直接寫入 ht[1](避免重復遷移)
-
刪除/更新:需同時操作兩個表
-
- ?擴容過程示例
-
假設初始狀態:
ht[0]
:size=4,used=3(負載因子 0.75)
插入新鍵值對 → used=4(負載因子=1)觸發擴容
操作 | ht[0] (size=4) | ht[1] (size=8) | rehashidx |
---|---|---|---|
初始狀態 | [k1,k2,k3,k4] | 空 | -1 |
觸發擴容 | [k1,k2,k3,k4] | 分配 size=8 | 0 |
遷移桶0(k1,k2) | [NULL,k3,k4] | [k1,k2] | 1 |
遷移桶1(k3) | [NULL,NULL,k4] | [k1,k2,k3] | 2 |
遷移桶2(k4) | 全空 | [k1,k2,k3,k4] | -1 (完成) |
1.3?雙端鏈表 (Linked List)
用途:?列表類型數據、發布訂閱、慢查詢、監視器、保存多個客戶端狀態等。
核心結構 (listNode
,?list
):
typedef struct listNode {struct listNode *prev;struct listNode *next;void *value;
} listNode;typedef struct list {listNode *head;listNode *tail;void *(*dup)(void *ptr); // 節點值復制函數void (*free)(void *ptr); // 節點值釋放函數int (*match)(void *ptr, void *key); // 節點值對比函數unsigned long len; // 鏈表長度
} list;
特點:?O(1) 復雜度訪問頭尾節點,支持雙向遍歷。內存開銷相對較高 (每個節點需要 prev/next 指針)。
1.4 跳躍表 (Skip List)
用途:?有序集合 (Sorted Set) 類型數據的底層實現之一。
設計目標:?在鏈表基礎上提供接近平衡樹的查找效率 (平均 O(logN)),且實現更簡單,范圍查詢更高效。
基本思想:將有序鏈表中的部分節點分層,每一層都是一個有序鏈表。
核心結構 (zskiplistNode
,?zskiplist
):
//跳躍表節點
typedef struct zskiplistNode {sds ele; // 成員值 (字符串SDS)double score; // 分值(排序依據)struct zskiplistNode *backward; // 后退指針 (雙向鏈表)struct zskiplistLevel {struct zskiplistNode *forward; // 前進指針,指向本層的下一個節點unsigned long span; // 跨度 (到下一個節點的距離),用于排名} level[]; // 柔性數組,動態定義層數
} zskiplistNode;typedef struct zskiplist {struct zskiplistNode *header, *tail; // 頭尾節點unsigned long length; // 節點總數 (不包括頭節點)int level; // 當前最大層數 (不包括頭節點層)
} zskiplist;
工作原理:
-
多層鏈表結構。最底層 (L0) 包含所有元素,是有序鏈表。
-
上層鏈表是下層的"快速通道",節點數更少。
-
插入時隨機確定節點的層數 (冪次定律,越高層概率越低)。
-
查找從最高層開始,比較?
score
?和?ele
,如果下一個節點大于目標值,則下降到下一層繼續查找。
優點:?支持高效范圍查詢 (ZRANGE
,?ZREVRANGE
),插入刪除相對平衡樹更簡單。
維度 | 跳躍表 (Skip List) | 平衡樹 (AVL/RB-Tree) |
---|---|---|
實現復雜度 | ? 簡單(無旋轉操作) | ? 復雜(需處理旋轉/再平衡) |
范圍查詢 | ? 天然支持(鏈表順序遍歷) | 🔶 需中序遍歷 |
并發友好性 | ? 更易實現無鎖并發 | ? 鎖粒度大 |
內存占用 | 🔶 略高(存儲多層指針) | ? 更緊湊 |
性能穩定性 | ? 無最壞情況(概率平衡) | ? 可能退化為 O(n) |
調試與維護 | ? 可視化直觀 | 🔶 結構復雜 |
1.5?壓縮列表 (ZipList) -?Redis 7.0 起被 Listpack 取代
用途 (歷史):?小規模列表、哈希、有序集合的緊湊存儲。
結構:?一塊連續內存,順序存儲多個元素。是一個字節數組,可以包含多個節點(entry)。每個節點可以保存一個字節數組或一個整數。
-
<zlbytes>
: 4 字節,列表總字節數(字節長度)。 -
<zltail>
: 4 字節,列表尾節點偏移量。 -
<zllen>
: 2 字節,節點數量 (超過 65535 時需遍歷)。 -
<entry>
: 若干節點。每個節點包含:-
<prevlen>
: 前一個節點的長度 (1 或 5 字節)。 -
<encoding>
: 內容編碼 (類型和長度,1/2/5 字節)。 -
<content>
: 實際數據。
-
-
<zlend>
: 1 字節,結束標志 0xFF(255)。
優點:?內存緊湊,減少碎片,適合小數據。
缺點 (連鎖更新):?修改中間節點可能導致后續所有節點的?<prevlen>
?需要擴展 (1->5 字節),引發多次內存重分配。這是被 Listpack 取代的主要原因。
1.6?緊湊列表 (Listpack) -?*Redis 5.0 引入,7.0 成為小規模列表/哈希/有序集合的默認*
設計目標:?解決 ZipList 的連鎖更新問題,更簡單高效。
結構:?也是一塊連續內存。
-
<總字節數>
: 4 字節。 -
<元素數量>
: 2 字節。 -
<元素項>
: 若干項。每個元素項:-
<encoding+data>
: 編碼 (包含數據類型和長度信息) + 實際數據。 -
<element-tot-len>
:?反向保存?當前元素項的總長度 (從?<element-tot-len>
?自身末尾到?<encoding>
?開頭)。這個長度字段是變長的 (1-5 字節)。
-
-
<結束符>
: 1 字節 (0xFF)。
關鍵改進:
-
消除連鎖更新:?每個元素項獨立存儲自身長度 (
<element-tot-len>
),修改一個元素不會影響其他元素的長度字段。 -
反向長度:?
<element-tot-len>
?存儲的是從自己末尾到前一項開頭的長度。遍歷時從后向前遍歷效率更高,查找時通常也是定位到某個位置再前后移動。
1.7?整數集合 (IntSet)
用途:?當集合 (Set) 類型的所有元素都是整數且數量不多時使用(保證元素不重復)。
結構:?一塊連續內存。
-
<encoding>
: 4 字節,指定元素類型 (INTSET_ENC_INT16
,?INT32
,?INT64
)。 -
<length>
: 4 字節,元素數量。 -
<contents>
: 按值大小升序排列的整數數組,類型由 encoding 決定。
特點:
-
內存效率極高。
-
插入新元素可能導致升級 (upgrade):如果新元素超出當前 encoding 的范圍,則將整個集合升級到更大的 encoding (如 INT16 -> INT32),重新分配內存并遷移數據。降級不支持。
1.8?快速列表 (QuickList) -?Redis 3.2 引入
用途:?列表 (List) 類型的默認底層實現、發布與訂閱、慢查詢、監視器等功能。
設計目標:?平衡內存效率和操作性能,替代早期僅使用 ZipList 或 LinkedList 的方案。
結構:?一個由 ZipList (或 Listpack) 構成的雙向鏈表。
typedef struct quicklist {quicklistNode *head;quicklistNode *tail;unsigned long count; // 所有節點內元素總數unsigned long len; // quicklistNode 節點數量,即ziplist的個數int fill: QL_FILL_BITS; // 單個節點最大容量 (ziplist大小限定,由 list-max-ziplist-size 配置)unsigned int compress: QL_COMP_BITS; // 兩端不壓縮的節點數 (節點壓縮深度設置,由 list-compress-depth 配置)...
} quicklist;typedef struct quicklistNode {struct quicklistNode *prev; // 指向上一個ziplist節點struct quicklistNode *next; // 指向下一個ziplist節點unsigned char *zl; // 指向底層 ZipList 或 Listpack (Redis 7.0+)unsigned int sz; // zl 指向的 ZipList/Listpack 的字節大小unsigned int count: 16; // 該節點內元素個數unsigned int encoding: 2; // 編碼方式:1--ziplist,2--quicklistLZF壓縮unsigned int container: 2; // 容器類型(存放數據方式):NONE, ZIPLIST, LISTPACK (Redis 7.0+)unsigned int recompress: 1; // 是否被壓縮過 (臨時狀態)...
} quicklistNode;
工作原理:
-
每個?
quicklistNode
?節點包含一個 ZipList 或 Listpack。每個ZipList(或Listpack)都能存儲多個元素。 -
fill
?參數控制單個節點最大元素數或大小。超過限制時分裂節點。 -
compress
?參數控制兩端不壓縮的節點數。中間節點可使用 LZF 壓縮節省內存。 -
插入/刪除操作盡量在節點內部的 ZipList/Listpack 完成。節點過大時分裂,過小時合并相鄰節點。
優點:?綜合了雙向鏈表 (易于兩端操作,節點分裂/合并) 和 ZipList/Listpack (內存局部性好,小數據高效) 的優勢,并通過壓縮進一步節省內存。
2、對象系統(RedisObject)
Redis 使用?redisObject
?結構體來統一表示數據庫中的所有值對象 (Value)。
2.1 結構信息概覽
typedef struct redisObject {unsigned type:4; // 對象類型 (REDIS_STRING, REDIS_LIST, REDIS_HASH, REDIS_SET, REDIS_ZSET)unsigned encoding:4; // 底層數據結構編碼 (如 REDIS_ENCODING_INT, REDIS_ENCODING_HT, REDIS_ENCODING_ZIPLIST...)unsigned lru:LRU_BITS; // LRU 時間戳 (或 LFU 計數與時間) - 用于內存淘汰int refcount; // 引用計數 - 用于內存回收 (垃圾回收)void *ptr; // 指向底層實現數據結構的指針 (SDS, dict, quicklist, zset...)
} robj;
1> 4位type
對象類型:REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。
當執行type命令時,便是讀取RedisObject的type字段獲取對象類型(type key)。
2> 4位encoding:查看對象編碼方式
3> 24位lru:lru記錄的是對象最后一次被命令程序訪問的時間。高16位存儲最后被訪問時間(分鐘級),低8位存儲最近訪問次數。
4> refcount:記錄對象被引用次數。對象創建時?refcount=1
,被新程序使用時?+1
,不再使用時?-1
。當?refcount==0
?時,對象占用的內存被回收。
5> ptr:指向具體的數據,比如:set hello world,ptr 指向包含字符串 world 的 SDS
2.2 核心作用
-
多態性:?相同類型的值對象 (
type
) 可以根據不同條件使用不同的底層數據結構 (encoding
)。命令的實現只需關注?type
?和?encoding
,調用對應的底層操作函數。 -
內存管理:?
refcount
?實現引用計數垃圾回收機制。lru
?記錄對象訪問信息,支撐 LRU/LFU 內存淘汰策略。 -
共享對象:?引用計數允許共享對象 (如小整數?
0
-9999
),節省內存。
2.3 類型與編碼映射
2.3.1?字符串 (String)
-
REDIS_ENCODING_INT
: 整數值 (直接用?ptr
?存儲,ptr
?被強制轉換為?long
)。 -
REDIS_ENCODING_EMBSTR
: 短字符串 (<=44字節,Redis 3.2+),redisObject
?和?SDS
?分配在連續內存。 -
REDIS_ENCODING_RAW
: 長字符串,動態?SDS
。
2.3.2?列表 (List)
-
REDIS_ENCODING_QUICKLIST
: 默認實現 (QuickList)。 -
(歷史)?
REDIS_ENCODING_ZIPLIST
?/?REDIS_ENCODING_LINKEDLIST
2.3.3?哈希 (Hash)
-
REDIS_ENCODING_LISTPACK
: 小哈希 (字段數/值大小未超配置閾值)。 -
REDIS_ENCODING_HT
: 大哈希 (字典)。
2.3.4?集合 (Set)
-
REDIS_ENCODING_INTSET
: 小整數集合。 -
REDIS_ENCODING_HT
: 大集合或包含非整數元素 (字典,值設為 NULL)。
2.3.5?有序集合 (Sorted Set)
-
REDIS_ENCODING_LISTPACK
: 小有序集合 (元素數/值大小未超閾值)。 -
REDIS_ENCODING_SKIPLIST
: 大有序集合 (跳躍表 + 字典)。字典提供 O(1) 成員到分值的查找,跳躍表提供按分值排序和范圍操作。兩者共享元素和分值。