String 數據結構 首先我來看下, Redis 中 String 的數據結構: 我們稱之為 SDS (Simple Dynamic String) 簡單動態字元串 struct sdshdr { //記錄buf數組中已經使用的位元組數(等價於字元串的長度strlen) int len; //記錄b ...
String 數據結構
首先我來看下, Redis 中 String 的數據結構:
我們稱之為 SDS (Simple Dynamic String) 簡單動態字元串
struct sdshdr {
//記錄buf數組中已經使用的位元組數(等價於字元串的長度strlen)
int len;
//記錄buf數組中未使用的位元組數(用於動態擴容)
int free;
//位元組數組,用於保存字元串
char buf[];
}
對比C語言字元串:
-
優化了獲取字元串長度的時間複雜度為: O(1)
因為 SDS 維護了一個 len 欄位,這個欄位的設置和更新是由 SDS 的 API 在執行時自動完成的。
-
杜絕緩衝區溢出
在使用 c 語言函數 strcat(s1, " Cluster") (在 s1 字元串後面拼接另一個字元串) 時,如果忘記提前為 s1 分配足夠的空間,那麼 strcat 函數執行之後, 將會導致s1後面的空間內容被意外修改。
而 Redis 在對 SDS 修改時,會提前檢查長度時候足夠,才執行相關操作, 如果長度不夠,會先擴展 s 的空間, 再執行 -
減少記憶體重分配
在 SDS 維護了 free 欄位,可以知道未使用的位元組數,這樣在動態擴充字元串時可以根據這個值判斷要不要重新分配記憶體
-
二進位安全
C語言是依靠 \0 作為字元串的結束標誌的,這意味著字元串中間不能再次包含 \0 字元, 而 SDS 記錄了字元串的實際長度, 所以它可以做更多的事情, 比如保存圖片
-
相容部分 C字元串函數
雖然 SDS 是 二進位安全的,但他們都遵守在字元串末尾額外分配一個位元組容納 \0 當結尾,這就是為了讓那些保存文本數據的 SDS 可以重用 <string.h> 庫
RedisObject 數據結構
在redis中存儲的每個對象都表示為一個redisObject,它記錄了這個對象的數據類型,編碼方式,指向實際內容的指針等
typedef struct redisObject {
// 類型 4bits (String,Hash,Set,List等)
unsigned type:4;
// 編碼方式 4bits (int, embstr, raw 等)
unsigned encoding:4;
// LRU 時間(相對於 server.lruclock) 24bits
unsigned lru:22;
// 引用計數 Redis裡面的數據可以通過引用計數進行共用 32bits
int refcount;
// 指向實際存儲的對象 (比如:指向一個SDS字元串對象) 64bits
void *ptr;
} robj;
String的三種編碼方式
-
int
當保存的值為整數且值的大小不超過long的範圍,使用整數存儲
-
embstr
當字元串長度不超過44個位元組時,使用embstr編碼
(只實現一次分配記憶體空間,只允許讀,若修改數據,就會轉成raw編碼) -
raw
大於44位元組時,用raw編碼
int編碼不必多說,為了數值計算方便
為什麼要分為 embstr 和 raw 呢?
首先來張圖,直觀感受下,這2種存儲方式的記憶體佈局:
- embstr 是連續存儲的,也就是說 RedisObject 對象頭 和 SDS 字元串的實際內容 是連續在一起存儲的,也就是 RedisObject 中的ptr 指向的內容,就緊跟著在後面 (只要分配1次記憶體)
- raw 是不連續存儲的, ptr 指向的內容,是單獨分配的(要分配2次記憶體)
這麼做的原因當然是為了性能考慮,Redis考慮到多數字元串,可能都不會很長, 這樣使用 embstr 只需分配一次記憶體(也很小),而且還是連續的存儲,性能很高,也有利於減少記憶體碎片
String編碼演示
- 使用
TYPE key
查看對象的數據類型 - 使用
OBJECT ENCODING key
查看對象的編碼方式
演示 int 編碼:
127.0.0.1:6379> set age 100
OK
127.0.0.1:6379> type age
string
127.0.0.1:6379> object encoding age
"int"
演示 embstr 編碼:
127.0.0.1:6379> SET name tom
OK
127.0.0.1:6379> TYPE name
string
127.0.0.1:6379> OBJECT ENCODING name
"embstr"
演示 raw 編碼:
127.0.0.1:6379> set name sdsdnjjkasjdnnjasjdnasldklaksdkkansndmasdulasndnkadashdahsdhkpasduasdybasbdvcfaffafkgsaas
OK
127.0.0.1:6379> type name
string
127.0.0.1:6379> object encoding name
"raw"