基本介紹
String 是最基本的 key-value
結(jié)構(gòu),key 是唯一標(biāo)識,value 是具體的值,value其實(shí)不僅是字符串, 也可以是數(shù)字(整數(shù)或浮點(diǎn)數(shù))以及二進(jìn)制等,value 最多可以容納的數(shù)據(jù)長度是 512M。
本文主要介紹的就是String類型中的SDS(Simple Dynamic String)
,俗稱簡單動態(tài)字符串。
內(nèi)部實(shí)現(xiàn)
要知道在redis中的String、map、list、set、zset類型都有各自的結(jié)構(gòu)體。
list
類型的結(jié)構(gòu)體如下:
class RedisListObject {
int type; // 數(shù)據(jù)類型標(biāo)識
int encoding; // 編碼方式標(biāo)識
int refcount; // 引用計(jì)數(shù)
int length; // 列表長度
RedisListNode head; // 列表頭節(jié)點(diǎn)指針
RedisListNode tail; // 列表尾節(jié)點(diǎn)指針
}
class RedisListNode {
RedisListObject obj; // 列表節(jié)點(diǎn)的值,可以是字符串對象等
RedisListNode prev; // 前一個(gè)節(jié)點(diǎn)指針
RedisListNode next; // 后一個(gè)節(jié)點(diǎn)指針
}
map
類型的結(jié)構(gòu)體如下:
class RedisHashObject {
int type; // 數(shù)據(jù)類型標(biāo)識
int encoding; // 編碼方式標(biāo)識
int refcount; // 引用計(jì)數(shù)
int length; // 哈希表中鍵值對的數(shù)量
RedisHashEntry[] table; // 哈希表數(shù)組,每個(gè)元素是一個(gè)鍵值對
}
class RedisHashEntry {
RedisObject key; // 鍵對象
RedisObject value; // 值對象
RedisHashEntry next; // 指向下一個(gè)哈希表節(jié)點(diǎn)的指針
}
set
類型的結(jié)構(gòu)體如下:
class RedisSetObject {
int type; // 數(shù)據(jù)類型標(biāo)識
int encoding; // 編碼方式標(biāo)識
int refcount; // 引用計(jì)數(shù)
int length; // 集合中元素的數(shù)量
RedisSetNode[] table; // 集合節(jié)點(diǎn)數(shù)組
}
class RedisSetNode {
RedisObject value; // 集合節(jié)點(diǎn)的值
RedisSetNode next; // 指向下一個(gè)集合節(jié)點(diǎn)的指針
}
String
類型的結(jié)構(gòu)體如下:
typedef struct redisObject {
unsigned type; // 類型標(biāo)識,用于表示對象的類型,如字符串、列表等
unsigned encoding; // 編碼標(biāo)識,用于表示對象的編碼方式,如 RAW、EMBSTR 等
int refcount; // 引用計(jì)數(shù),記錄對象被引用的次數(shù)
void *ptr; // 指向底層實(shí)際數(shù)據(jù)的指針
}
可以發(fā)現(xiàn)每種結(jié)構(gòu)體都有3個(gè)共同的屬性即type
、encoding
、refcount
。
本文我們主要討論的是String
類型的存儲結(jié)構(gòu),而對于String
類型來說,encoding編碼格式有3種,即int
、embstr
和row
,不同的編碼格式對應(yīng)的字符串的存儲類型也是不同的。
編碼為int
如果一個(gè)字符串對象保存的是整數(shù)值
,并且這個(gè)整數(shù)值可以用long
類型來表示時(shí):
- 字符串結(jié)構(gòu)體中的encoding設(shè)置為
int
。 - 字符串結(jié)構(gòu)體中ptr屬性轉(zhuǎn)化為
long
類型,且直接存儲對應(yīng)整形value值。
typedef struct redisObject {
unsigned type; // 類型標(biāo)識:這里為字符串
unsigned encoding; // 編碼標(biāo)識:int
int refcount; // 引用計(jì)數(shù),記錄對象被引用的次數(shù)
long ptr; // 直接存儲對應(yīng)的整形值,比如132132
}
編碼為embstr
如果字符串對象保存的是一個(gè)字符串
,并且這個(gè)字符申的長度小于等于 32 字節(jié)(redis 2.+版本)時(shí):
- 字符串結(jié)構(gòu)體中的encoding設(shè)置為
embstr
。 - 字符串結(jié)構(gòu)體中ptr屬性轉(zhuǎn)化為
char []
字符數(shù)組類型,且直接存儲對應(yīng)的字符串。
typedef struct redisObject {
unsigned type; // 類型標(biāo)識:這里為字符串
unsigned encoding; // 編碼標(biāo)識:embstr
int refcount; // 引用計(jì)數(shù),記錄對象被引用的次數(shù)
char[] ptr; // 直接存儲對應(yīng)的整形值,比如['a','b','c']
}
編碼為row
如果字符串對象保存的是一個(gè)字符串
,并且這個(gè)字符串的長度大于 32 字節(jié)(redis 2.+版本)時(shí):
- 字符串結(jié)構(gòu)體中的encoding設(shè)置為
row
。 - 字符串結(jié)構(gòu)體中ptr將指向一個(gè)
SDS
結(jié)構(gòu)體,該結(jié)構(gòu)體如下:
struct sdshdr {
unsigned int len; // 已使用的字節(jié)數(shù)
unsigned int free; // 剩余的字節(jié)數(shù)
char buf[]; // 字符數(shù)組,用于存儲字符串?dāng)?shù)據(jù)
};
-
len
: 記錄當(dāng)前字符串的長度,表示 buf 數(shù)組中已使用的字節(jié)數(shù)。這個(gè)值可以直接獲取,使得獲取字符串長度的操作具有 O(1) 復(fù)雜度。 -
free
: 記錄 buf 數(shù)組中未使用的字節(jié)數(shù),即空余空間的大小。當(dāng)執(zhí)行字符串追加操作(例如 APPEND)時(shí),如果追加的內(nèi)容長度大于 free 的大小,SDS 會自動擴(kuò)展 buf 的大小,確保足夠的空間容納新的內(nèi)容,避免緩沖區(qū)溢出。 -
buf(buffer
): 用于存儲字符串內(nèi)容的字符數(shù)組
。
此時(shí)的String
類型的結(jié)構(gòu)體如下:
typedef struct redisObject {
unsigned type; // 類型標(biāo)識:這里為字符串
unsigned encoding; // 編碼標(biāo)識:row
int refcount; // 引用計(jì)數(shù),記錄對象被引用的次數(shù)
sdshdr *ptr; // 指向一個(gè)SDS結(jié)構(gòu)體,該結(jié)構(gòu)體存儲存儲對應(yīng)的字符串信息
}
總結(jié)
SDS 和我們認(rèn)識的 C 字符串不太一樣,之所以沒有使用 C 語言的字符串表示,因?yàn)?SDS 相比于 C 的原生字符串:
- SDS 不僅可以保存
文本數(shù)據(jù)
,還可以保存二進(jìn)制數(shù)據(jù)
。因?yàn)?SDS 使用 len 屬性的值而不是空字符來判斷字符串是否結(jié)束,并且 SDS 的所有 API 都會以處理二進(jìn)制的方式來處理 SDS 存放在 buf[] 數(shù)組里的數(shù)據(jù)。所以 SDS 不光能存放文本數(shù)據(jù),而且能保存圖片、音頻、視頻、壓縮文件這樣的二進(jìn)制數(shù)據(jù)。 - SDS 獲取字符串長度的時(shí)間復(fù)雜度是
O(1)
。因?yàn)?C 語言的字符串并不記錄自身長度,所以獲取長度的復(fù)雜度為 O(n);而 SDS 結(jié)構(gòu)里用 len 屬性記錄了字符串長度,所以復(fù)雜度為 O(1)。 - Redis 的 SDS API 是安全的,拼接字符串不會造成緩沖區(qū)溢出。因?yàn)?SDS 在拼接字符串之前會檢查 SDS 空間是否滿足要求,如果空間不夠會
自動擴(kuò)容
,所以不會導(dǎo)致緩沖區(qū)溢出的問題。