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】