Redis基礎知識(學習筆記11--SDS)

东山絮柳仔發表於2024-06-30

1.SDS介紹(為什麼要提出SDS)

無論是Redis的key還是Value,其基礎資料型別都是字串。例如,Hash型Value的field 與 Value的型別、List型、Set型、ZSet型Value的元素都是字串。雖然Redis是使用標準C語言開發的,但並沒有直接使用C語言中傳統的字串表示,而是自定義了一種字串。這種字串本身的結構比較簡單,但功能卻非常強大,稱為簡單動態字串,Simple Dynamic String,簡稱SDS。

注意:Redis中的所有字串並不都是SDS,也會出現C字串。C字串只會出現在字串“字面常量”中,並且該字串不可能發生變更。例如,Redis 返回的結果中包含的字串就是C語言的字串。

2.SDS結構

SDS 不同於C字串。C字串本身是一個以雙引號括起來,以空字串‘\0’結尾的字串序列。但SDS是一個結構體,定義在Redis安裝目錄下的 src/sds.h中。

struct sdshdr {
//位元組陣列,用於儲存字串
char buf[];

//buf[]中已使用位元組數量,稱為SDS的長度
int len;

//buf[]中尚未使用的位元組數量
int free;

}

例如執行 set countey "China" 命令時,鍵country 與值 ”China“ 都是SDS型別的,只不過一個是SDS的變數,一個是SDS的字面常量,”China“在記憶體中的結構如下:

透過以上結構可以看出來,SDS的buf值實際上是一個C字串,包含空字串‘\0’共佔6個位元組。但SDS的len是不包含‘\0’的。

在實際的使用過程中,經常會做一些預分配,變成下面這種結構。

該結構與前面不同的是,這裡面有3位元組未使用空間。

補充

type keyname

可以檢視那種型別。

object encoding keyname

可以檢視key對應的value在記憶體中的儲存型別。例如,如果返回的是"embstr",就是我們前面說的SDS。

3.SDS的優勢

C字串使用len+1長度的字串陣列來表示實際長度為len的字串,字串陣列最後以空字串’\0‘結尾,表示字串結尾。這種結構簡單,但不能滿足Redis對字串功能性、安全性及高效性等的要求。

(1)防止“字串長度獲取”的效能瓶頸

對於C字串,若要獲取其長度,則必須要透過遍歷整個字串才能獲取得到。對於超長字串的遍歷,會成為系統的效能瓶頸。而由於SDS結構體中直接就存放著字串的長度資料,所以對於獲取字串長度需要消耗的系統效能,特別是在長字串的情況下,不會成為Redis的效能瓶頸。

(2)保障二進位制的安全

C字串中只能包含符合某種編碼格式的字元,例如ASCII、UTF-8等,並且除了字串尾外,其他位置是不能包含空字串’\0‘的,否則該字串就會被程式誤解為提前結束,而在圖片、音訊、影片、壓縮檔案、office 檔案等二進位制資料中常以空字串'\0'作為分隔符的情況是很常見的,故而在C字串中是不能儲存圖片、音訊、影片、壓縮檔案、office 檔案等二進位制資料的。

但SDS不是以空字串'\0'作為結束標誌的,其是透過len屬性來判斷字串是否結束的。所以,對於程式處理SDS中的字串資料,無需對資料做任何限制、過濾、假設,只需讀取即可。資料寫入的是什麼,讀到的就是什麼。

(3)減少記憶體再分配次數

SDS採用了空間預分配策略惰性空間釋放策略來避免記憶體再分配問題。

空間預分配策略是指,每次SDS進行空間擴充套件時,程式不但為其分配所需的空間,還會為其分配額外的未使用空間,以減少記憶體再分配次數。而額外分配的未使用空間大小取決於空間擴充套件後SDS的len屬性值。

***如果len屬性值小於1M,那麼分配的未使用空間free的大小與len相同。

***如果len的屬性值大於等於1M,那麼分配的未使用空間free的大小就是1M。

SDS對於空間釋放採用的時惰性空間釋放策略。該策略是指,SDS字串長度如果縮短,那麼多出的未使用空間將暫時不釋放,而是增加到free中。所以後期擴充套件SDS時減少記憶體再次分配。

如果要釋放SDS的未使用空間,則可以透過sdsRemoveFreeSpace()函式來釋放。

(4)相容C函式

Redis中提供了很多SDS的API,以方便使用者對SDS進行二次開發。為了能夠相容C函式,SDS的底層陣列buf[]中的字串仍以空字串'\0'結尾。

4 常用的SDS操作函式

函式 功能描述
sdsnew() 使用指定的C字串建立一個SDS
sdsempty(); 建立一個不包含任何字串資料的SDS
sdsdup(const sds s); 複製字串(建立一個指定SDS的副本)
sdsfree() 釋放指定的SDS
sdsclear() 清空指定SDS的字串內容
sdslen() 獲取指定SDS的已使用空間len值
sdssaveall() 獲取指定SDS的未使用空間free值
sdsMakeRoomFor() 使指定的SDS的free空間增加指定的大小
sdsRemoveFreeSpace() 釋放指定SDS的free空間
sdscat() 將指定的C字串拼接到指定SDS字串末尾
sdscatsds() 將指定的SDS的字串拼接到另一個指定的SDS字串末尾
sdscpy() 將指定的C字串賦值到指定的SDS中,覆蓋原SDS字串內容
sdsgrowzero(sds s, size_t len); 擴充套件SDS字串到指定長度,這個擴充套件是使用空字串’\0‘填充
sdsrange(sds s, int start, int end) 擷取指定SDS中指定範圍內的字串
sdstrim(sds s, const char *cset); 在指定SDS中刪除所有隻當C字串中出現的所有字元
sdsemp() 對比兩個給定SDS字串是否相同
sdstolow() 將指定SDS字串中的所有字面變為小寫
sdstouper() 將指定SDS字串中的所有字面變為大寫

學習參閱宣告

【Redis影片從入門到高階】

https://www.bilibili.com/video/BV1U24y1y7jF?p=11&vd_source=0e347fbc6c2b049143afaa5a15abfc1c】

相關文章