string類型的數(shù)據(jù)結(jié)構(gòu)SDS到底是什么東西

基本介紹

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、embstrrow,不同的編碼格式對應(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ū)溢出的問題。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容