Redis的數據結構之SDS
Redis沒有使用C語言風格的字符串, 而是使用了一種叫SDS的簡單動態字符串。
C風格的字符串用來作爲字符串字面量, 比如打印日誌。當表示一個可以被修改的字符串的時候, redis會用SDS表示。
redis是key_value型數據庫, 它的key和value都是一個對象。
SDS除了保存數據庫中的字符串外還用作緩衝區(buffer): AOF模塊中的AOF緩衝區, 以及客戶端狀態中的輸入緩衝區。
SDS的定義
//sds.h/sdshdr 結構表示一個SDS值
struct sdshdr {
int len; //記錄buf中使用的字節數量
int free; //記錄還未使用的字節數量
int buf[]; //字節數組用來保存字符串
};
需要注意的是: buf中的最後有’\0’, 並且這個字符沒有計入len中, 這樣設計的原因是爲了遵循C語言的標準, 這樣就可以使用C語言的一些函數. 比如可以直接 printf("%s", s->buf);
SDS和C語言字符串的區別
-
常數時間複雜度獲取字符串長度
C語言使用一個N+1長度的數組表示長度爲N的字符串, 求C語言字符串長度需要遍歷整個數組, 時間複雜度爲O(N). 而 SDS 使用結構體中的len字段可以實現O(1)的時間複雜度求字符串的長度.
-
杜絕緩衝區溢出
C語言不記錄字符串長度, 如果拼接字符串的時候分配的內存空間不夠的話就會造成緩衝區溢出. 而SDS記錄未使用的緩存區空間(free字段), 所以在拼接字符串的時候就會先檢查內存夠不夠, 不夠的話就擴展內存再進行拼接操作.
-
減小修改字符串時帶來的內存重新分配次數
SDS的free字段會對SDS的空間進行預分配, 這樣就可以避免像C語言字符串那樣改變時需要重新分配內存的尷尬.
-
二進制安全
C語言字符串中字符必須符合某種編碼, 除了字符串末尾之外不能包含空格, 否則容易被當做字符串結尾.這樣就限制了保存二進制的能力.
而redis爲了確保可以在各種不同的場景中使用, Redis的API都是二進制安全的. 也就是說寫進去什麼樣, 讀出來就是什麼樣.
-
兼容部分C語言字符串
SDS以’\0’結尾, 所以可以複用C語言一些現成的函數, 比如strcasecmp函數.
SDS的主要API
函數 | 作用 | 時間複雜度 |
---|---|---|
sdsnew | 創建一個包含給定C字符串的SDS | O(N) |
sdsempty | 創建一個空的SDS | O(1) |
sdsfree | 釋放給定的SDS | O(N) |
sdslen | 返回SDS已經使用過的空間字符數 | O(1) |
sdsavail | 返回SDS中未使用的空間字符數 | O(1) |
sdsdup | 創建一個給定SDS的副本 | O(N) |
sdsclear | 清空SDS中字符串保存的內容 | O(1) 惰性空間釋放 |
sdscat | 將C字符串拼接到SDS字符串末尾 | O(N) |
sdscatsds | 將SDS拼接到另一個SDS中 | O(N) |
sdscpy | 將給定的C字符串複製並覆蓋到SDS中的字符串 | O(N) |
sdsgrowzero | 用空字符將SDS擴展至給定的長度 | O(N) |
sdsrange | SDS區間內的數據保留, 區間之外的數據覆蓋或清除 | O(N) |
sdstrim | 傳SDS和C字符串, 移除SDS左右兩邊分別在C出現的字符 | O(M*N) |
sdscmp | 比較兩個SDS是否相等 | O(N) |