sds

簡介

在redis內(nèi)部,string數(shù)據(jù)結(jié)構(gòu)的value主要以int诉植、sds作為結(jié)構(gòu)存儲。int用來存放整型昵观,sds用來存放字節(jié)晾腔、字符串、浮點型數(shù)據(jù)啊犬。

sds結(jié)構(gòu)分析

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

sds一共有5種類型的header(sdshdr5 不再被使用了)灼擂。使得不同長度的字符串可以使用不同大小的header,從而節(jié)省內(nèi)存觉至。

接下來我們定義一個通用的sds結(jié)構(gòu)剔应,如下:

struct  sdshdr {
    XXX len; /* used */
    XXX alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
  • len:表示字符串的真正長度(不包含NULL結(jié)束符);
  • alloc:表示字符串的最大容量(不包含頭部和NULL結(jié)束符)语御;
  • flags:占用一個字節(jié)峻贮,其中的最低3個bit用來表示header的類型,其余5個bit未使用沃暗。對應(yīng)類型如下:
#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
  • buf:字符數(shù)組月洛,存儲實際內(nèi)容。SDS 遵循 C 字符串以空字符結(jié)尾的慣例孽锥, 保存NULL字符的 1 字節(jié)空間不計算在SDS的len屬性嚼黔、alloc屬性里面细层, 并且為空字符分配額外的 1 字節(jié)空間。

sds優(yōu)勢

C 語言使用長度為 N+1 的字符數(shù)組來表示長度為 N 的字符串唬涧, 并且字符數(shù)組的最后一個元素總是NULL字符 '\0' 疫赎。這種簡單的字符串表示方式, 并不能滿足 Redis 對字符串在安全性碎节、效率捧搞、以及功能方面的要求。

常數(shù)復(fù)雜度獲取字符串長度

C 字符串并不記錄自身的長度信息狮荔, 所以為了獲取一個 C 字符串的長度胎撇, 程序必須遍歷整個字符串, 對遇到的每個字符進(jìn)行計數(shù)殖氏, 直到遇到代表字符串結(jié)尾的空字符為止晚树, 這個操作的復(fù)雜度為 O(N);
sds記錄字符串長度雅采,可以直接返回len爵憎,復(fù)雜度為 O(1);

杜絕緩沖區(qū)溢出

C 字符串不記錄自身長度帶來的另一個問題是容易造成緩沖區(qū)溢出婚瓜。


image.png

如上圖所示宝鼓,str1="hello",str2="world"巴刻,str1與str2存儲在連續(xù)的內(nèi)存空間愚铡,接著對str1拼接“&hello”(c語言提供<string.h>/strcat 函數(shù)可以將 src 字符串中的內(nèi)容拼接到 dest 字符串的末尾:char *strcat(char *dest, const char *src);),可以看到原先str2="world"被修改了冈涧。

sds的空間分配策略完全杜絕了發(fā)生緩沖區(qū)溢出的可能性: 當(dāng)sds API 需要對sds進(jìn)行修改時茂附, API 會先檢查sds的空間是否滿足修改所需的要求, 如果不滿足的話督弓, API 會自動將sds的空間擴(kuò)展至執(zhí)行修改所需的大小营曼, 然后才執(zhí)行實際的修改操作, 所以使用sds既不需要手動修改sds的空間大小愚隧, 也不會出現(xiàn)前面所說的緩沖區(qū)溢出問題蒂阱。

減少修改字符串時帶來的內(nèi)存重分配次數(shù)

每次增長或者縮短一個 C 字符串, 程序都總要對保存這個 C 字符串的數(shù)組進(jìn)行一次內(nèi)存重分配操作狂塘。內(nèi)存重分配涉及復(fù)雜的算法录煤, 并且可能需要執(zhí)行系統(tǒng)調(diào)用, 所以它通常是一個比較耗時的操作荞胡。

在sds中妈踊, buf 數(shù)組的長度不一定就是字符數(shù)量加一, 數(shù)組里面可以包含未使用的字節(jié)泪漂, 而這些字節(jié)的數(shù)量就由 SDS 的 alloc屬性 - len屬性來決定廊营。
sds通過空間預(yù)分配和惰性空間釋放兩種優(yōu)化策略來減少修改字符串時帶來的內(nèi)存重分配次數(shù)歪泳。

空間預(yù)分配

空間預(yù)分配策略如下:

  • 如果對sds進(jìn)行修改之后, len < 1 MB 露筒, 則alloc = 2 * len呐伞,buf長度 = 2*len + 1;
  • 如果對sds進(jìn)行修改之后慎式, len >= 1 MB 伶氢, 則alloc = len + 1MB,buf長度 = len + 1MB + 1byte瘪吏;

通過空間預(yù)分配策略癣防, Redis 可以減少連續(xù)執(zhí)行字符串增長操作所需的內(nèi)存重分配次數(shù)。

惰性空間釋放

惰性空間釋放用于優(yōu)化sds的字符串縮短操作: 當(dāng)需要縮短SDS保存的字符串時掌眠,程序并不立即使用內(nèi)存重分配來回收縮短后多出來的字節(jié)劣砍, 而是先預(yù)留著,并等待將來使用扇救。

當(dāng)然, sds也提供了相應(yīng)的API香嗓,讓我們可以在有需要時迅腔,真正地釋放 sds里面的未使用空間, 所以不用擔(dān)心惰性空間釋放策略會造成內(nèi)存浪費靠娱。

二進(jìn)制安全

對于C語言來說沧烈,C 字符串中的字符必須符合某種編碼(比如 ASCII), 并且除了字符串的末尾之外像云, 字符串里面不能包含NULL字符锌雀, 否則最先被程序讀入的空字符將被誤認(rèn)為是字符串結(jié)尾 —— 這些限制使得 C 字符串只能保存文本數(shù)據(jù), 而不能保存像圖片迅诬、音頻腋逆、視頻、壓縮文件這樣的二進(jìn)制數(shù)據(jù)侈贷。

sds的 API 都是二進(jìn)制安全的(binary-safe): 所有sds API 都會以處理二進(jìn)制的方式來處理sds存放在buf數(shù)組里的數(shù)據(jù)惩歉, 程序不會對其中的數(shù)據(jù)做任何額外的處理,數(shù)據(jù)在寫入時是什么樣的俏蛮,它被讀取時就是什么樣撑蚌。也就是說相比C語言字符串,SDS會通過len來限制讀取長度搏屑,而非“\0”争涌,所以保證了二進(jìn)制安全。

因此sds既可以保存文本數(shù)據(jù)辣恋, 又能保存二進(jìn)制數(shù)據(jù)亮垫。

兼容部分 C 字符串函數(shù)

通過遵循C字符串以NULL字符結(jié)尾的慣例模软, sds可以使用部分 <string.h> 庫中的函數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末包警,一起剝皮案震驚了整個濱河市撵摆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌害晦,老刑警劉巖特铝,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異壹瘟,居然都是意外死亡鲫剿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門稻轨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灵莲,“玉大人,你說我怎么就攤上這事殴俱≌常” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵线欲,是天一觀的道長明场。 經(jīng)常有香客問我,道長李丰,這世上最難降的妖魔是什么苦锨? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮趴泌,結(jié)果婚禮上舟舒,老公的妹妹穿的比我還像新娘。我一直安慰自己嗜憔,他們只是感情好秃励,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痹筛,像睡著了一般莺治。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帚稠,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天谣旁,我揣著相機(jī)與錄音,去河邊找鬼滋早。 笑死榄审,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杆麸。 我是一名探鬼主播搁进,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼浪感,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饼问?” 一聲冷哼從身側(cè)響起影兽,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎莱革,沒想到半個月后峻堰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡盅视,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年捐名,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闹击。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡镶蹋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赏半,到底是詐尸還是另有隱情贺归,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布断箫,位于F島的核電站牧氮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瑰枫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一丹莲、第九天 我趴在偏房一處隱蔽的房頂上張望光坝。 院中可真熱鬧,春花似錦甥材、人聲如沸盯另。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸳惯。三九已至,卻和暖如春叠萍,著一層夾襖步出監(jiān)牢的瞬間芝发,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工苛谷, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留辅鲸,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓腹殿,卻偏偏與公主長得像独悴,于是被迫代替她去往敵國和親例书。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容