Redis 原理 - String
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"
String的常用命令
- SET: 添加或修改一個已經存在的String類型的鍵值對
- GET: 根據key獲取String類型的value
- INCR: 讓一個整形存儲的String類型的value自增1
- INCRBY: 讓一個整形存儲的String類型的value自增指定的步長, 例如 incrby num 2, 讓num的值自增2
- INCRBYFLOAT: 讓一個浮點類型的數值自增指定的步長
- SETNX: 添加一個String類型的鍵值對,前提是這個key不存在,否則不執行(可用于實現分布式鎖)
- SETEX: 添加一個String類型的鍵值對,并且指定超時時間
- 更多 String 命令,請查閱 官方文檔

浙公網安備 33010602011771號