第二章 簡單動態(tài)字符串

redis沒有直接使用c語言傳統(tǒng)的字符串表示(以空字符結(jié)尾的字符數(shù)組),而是自己構(gòu)建了一種“簡單動態(tài)字符串”(simple dynamic string 谍珊,SDS)用作Redis的默認字符串表示捅暴。

作用

  • 保存數(shù)據(jù)庫中字符串值
  • 用作緩沖區(qū):AOF模塊中的AOF緩沖區(qū)盲赊,以及客戶端狀態(tài)中的輸入緩沖區(qū)

2.1 SDS的定義

數(shù)據(jù)結(jié)構(gòu)

struct sdshdr{
  //記錄buf數(shù)組已使用字節(jié)的數(shù)量碟渺,等于SDS所保存的字符串長度
  int len;
  //記錄buf數(shù)組中未使用的字節(jié)數(shù)量
  int free;
  //字節(jié)數(shù)組叔扼,用于保存字符串
  char buf[]
}

實例圖

WechatIMG48.jpeg
  • free屬性值為0览徒,表示sds沒有分配任何未使用空間
  • len屬性值為5狈定,表示sds保存了一個5字節(jié)長的字符串
  • buf屬性是一個char類型的數(shù)組,分別保存了'R','e',...'s' 五個字符习蓬,最后一個字節(jié)保存空字符 '\0';

SDS遵循C字符串以空字符結(jié)尾的慣例纽什,保存空字符的1字節(jié)空間不計算在SDS的len屬性里面;為空字符分配額外的1字節(jié)空間躲叼,以及添加空字符到字符串末尾操作芦缰,都是由SDS函數(shù)自動完成的;遵循空字符串結(jié)尾好處是枫慷,可以直接重用一部分c字符串函數(shù)庫里的函數(shù)让蕾。例:printf() 函數(shù)等

2.2 SDS 與 C 字符串的區(qū)別

C語言使用長度為N+1的字符數(shù)組,表示長度為N的字符串流礁,并且最后一位元素總是空字符'\0'涕俗,這種表達方式不能滿足Redis對字符串在安全性,效率神帅,以及功能上面的要求

C字符串 SDS
獲取字符串長度的復雜度為O(n) 獲取字符串長度的復雜度為O(1)
API是不安全的再姑,會造成緩沖區(qū)溢出 API是安全的,不會造成緩沖區(qū)溢出
修改字符串長度N次必然需要執(zhí)行N次內(nèi)存重分配 修改字符串長度N次最多修改執(zhí)行N次內(nèi)存重分配
只能保存文本數(shù)據(jù) 可以保存文本或者二進制數(shù)據(jù)
可以使用所有<string.h>庫中的函數(shù) 可以使用一部分<string.h>庫中的函數(shù)?

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

c字符串不記錄自身的長度信息找御,獲取一個c字符串的長度必須遍歷整個字符串元镀,遇到結(jié)尾的空字符串為止绍填,復雜度為O(N)。
SDS在len屬性中記錄了SDS本身的長度栖疑,復雜度為O(1)讨永。(?設置和更新SDS長度是由SDS的API執(zhí)行時自動完成的)

2.2.2 杜絕緩沖區(qū)溢出

假設有兩個在內(nèi)存中相鄰的兩個c字符串s1和s2,其中s1 保存了“Redis” 遇革,s2保存了“MongoDB”卿闹,通過執(zhí)行strcat(s1, "Cluster")(拼接函數(shù)) ,若忘記在執(zhí)行strcat之前為s1分配足夠的內(nèi)存空間萝快,在執(zhí)行完strcat函數(shù)后锻霎,s1的數(shù)據(jù)將溢出到s2所在的空間中,導致s2保存的內(nèi)容被意外的修改

SDS的內(nèi)存空間完全的杜絕了緩沖區(qū)溢出的可能性:當SDS api 需要對SDS進行操作的時候揪漩,會先檢查SDS的空間是否滿足修改所需的要求旋恼;如果不滿足的話,API會自動將SDS的空間擴大至所需要的大小奄容,然后才執(zhí)行修改操作冰更。所有,SDS不需要手動的修改SDS空間大小昂勒,也不會出現(xiàn)c語言的緩沖區(qū)溢出問題蜀细。

修改前

WechatIMG50.jpeg

修改后

WechatIMG49.jpeg

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

c字符串的底層實現(xiàn)總是一個N+1個字符長的數(shù)組,所以每次增長或縮短一個c字符串,程序都總要對保存這個c字符串的數(shù)組進行一次內(nèi)存重分配操作:

  • 增長字符串操作,比如拼接祝闻,執(zhí)行這個操作之前,需要通過內(nèi)存重新分配來擴展底層數(shù)組的空間大小涣觉,如果忘了會產(chǎn)生緩沖區(qū)溢出。
  • 縮短字符串操作血柳,比如截斷官册,執(zhí)行這個操作之后,需要通過內(nèi)存重分配釋放不再使用的那部分空間难捌,如果忘了就會產(chǎn)生內(nèi)存泄漏膝宁。

c字符串內(nèi)存重新分配涉及復雜的算法,需要執(zhí)行系統(tǒng)的調(diào)用根吁,通常是一個比較耗時的操作:

  • 一般程序中员淫,修改字符串長度不太常出現(xiàn),每次修改都執(zhí)行一次內(nèi)存重分配是可以接受的击敌。
  • Redis作為數(shù)據(jù)庫介返,用于速度要求嚴格,數(shù)據(jù)頻繁修改的場合,每次修改字符串的長度都需要執(zhí)行一次內(nèi)存分配的話圣蝎,光是執(zhí)行內(nèi)存重分配的時間就會占去修改字符串所用時間的一大部分刃宵,修改頻繁的話,還會對性能造成影響徘公、

sds為了避免c字符串的這種缺陷牲证,通過未使用空間解除了字符串長度和底層數(shù)組長度的之間的關(guān)聯(lián);在sds中buf數(shù)組的長度不一定就是字符數(shù)量加一关面,數(shù)組里面包含了未使用的字節(jié)坦袍,由free屬性記錄;
通過未使用空間缭裆,實現(xiàn)了 空間預分配 和 惰性空間釋放 兩種優(yōu)化政策:

2.2.3.1 空間預分配

用于優(yōu)化sds字符串增長操作键闺,當進行空間擴展的時候,不僅會為sds分配修改所需要的空間澈驼,還會分配額外的未使用空間。
其中筛武,額外分配未使用空間數(shù)量缝其,由一下公式?jīng)Q定:

  • sds len屬性 < 1MB,程序分配和len屬性同樣大小的未使用空間;
  • sds len屬性 >= 1MB徘六,程序分配1MB的未使用空間内边;

通過空間預分配策略,Redis可以減少連續(xù)執(zhí)行字符串增長操作所需的內(nèi)存重分配次數(shù)待锈。
在擴展sds空間之前漠其,api會先檢查未使用的空間是否足夠,足夠的話竿音,直接使用未使用空間和屎,無須執(zhí)行內(nèi)存重分配。
通過這種預分配策略春瞬,將內(nèi)存分配次數(shù)從必定N次降低為最多N次

2.2.3.2 惰性空間釋放

用于優(yōu)化sds字符串縮短操作:通過api縮短sds保存的字符串時柴信,并不立即使用內(nèi)存重分配來回收縮短后多出來的字節(jié),而是通過free屬性將這些字節(jié)的數(shù)量記錄下來宽气,等待將來使用随常;


WechatIMG52.jpeg

執(zhí)行 sdstrim(s, "XY"); //移除sds字符串中所有'X' 和 'Y'

WechatIMG51.jpeg

執(zhí)行sdstrim之后的sds并沒有釋放多出來的8個字節(jié)空間,而是將這8個字節(jié)空間作為未使用空間保留在了sds里面萄涯,將來要執(zhí)行增長操作的話绪氛,未使用空間就能派上用場。

通過惰性空間釋放策略涝影,sds避免了縮短字符串所需的內(nèi)存重分配操作枣察,并為將來可能有的增長操作提供了優(yōu)化。
sds也提供了相應的api袄琳,在我們需要的時候询件,真正的釋放sds未使用空間燃乍,不用擔心惰性空間釋放策略會造成內(nèi)存的浪費

2.2.4 二進制安全

c字符串中的字符必須符合某種編碼(ASCII) ,并且除了字符串末尾之外宛琅,其他地方不能包含空字符刻蟹,否則最先被程序讀入的空字符將被誤以為是字符串結(jié)尾,也限制了c字符串只能保存文本數(shù)據(jù)嘿辟,不能保存圖片舆瘪,音頻,視頻红伦,壓縮文件這樣的二進制數(shù)據(jù)英古。

為了確保Redis適用于不同的使用場景,SDS api 都是二進制安全的昙读,所有api都會以處理二進制方式來處理sds存放在buf數(shù)組里的數(shù)據(jù)召调,并且不會對其中的數(shù)據(jù)做任何限制,過濾蛮浑;sds使用的len屬性的值唠叛,而不是空字符來判斷字符串是否結(jié)束。

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

雖然sds的api都是二進制安全的沮稚,但一樣遵循c字符串以空字符結(jié)尾的慣例艺沼,為保證保存文本數(shù)據(jù)可以重用一部分<string.h>定義的函數(shù)

2.3 SDS API

函數(shù):作用 時間復雜度
sdsnew: 創(chuàng)建一個包含給定c字符串的SDS O(n)
sdsempty: 創(chuàng)建一個不包含任何內(nèi)容的空SDS O(1)
sdsfree: 釋放給定的sds O(n)
sdslen: 返回sds的已使用空間字節(jié)數(shù) 通過讀取len屬性 O(1)
sdsavail: 返回sds未使用的空間字節(jié)數(shù) 通過讀取free屬性 O(1)
sdsdup: 創(chuàng)建一個給定sds的副本 O(n)
sdsclear: 清空sds保存的字符串內(nèi)容 惰性空間釋放策略 O(1)
sdscat: 將給定的c字符串拼接到sds字符串末尾 O(n)
sdscatsds: 將給定的sds字符串拼接到另一個sds字符串的末尾 O(n)
sdscpy: 給定的c字符串復制到sds里面,覆蓋sds原有的字符串 O(n)
sdsgrowzero: 用空字符串將sds擴展至給定長度 O(n)
sdsrange: 保留給定區(qū)間內(nèi)的數(shù)據(jù)蕴掏,不在區(qū)間內(nèi)數(shù)據(jù)會被覆蓋或清除 O(n)
sdstrim: 接受一個sds 和一個c字符串作為參數(shù)障般,從sds左右兩端分別移除所有在c字符串中出現(xiàn)過的字符 O(m*n) m為sds的長度,n為給定c字符串長度
sdscmp: 對比兩個字符串是否相同 O(n)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盛杰,一起剝皮案震驚了整個濱河市挽荡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌饶唤,老刑警劉巖徐伐,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異募狂,居然都是意外死亡办素,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門祸穷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來性穿,“玉大人,你說我怎么就攤上這事雷滚⌒柙” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呆万。 經(jīng)常有香客問我商源,道長,這世上最難降的妖魔是什么谋减? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任牡彻,我火速辦了婚禮,結(jié)果婚禮上出爹,老公的妹妹穿的比我還像新娘庄吼。我一直安慰自己,他們只是感情好严就,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布总寻。 她就那樣靜靜地躺著,像睡著了一般梢为。 火紅的嫁衣襯著肌膚如雪渐行。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天抖誉,我揣著相機與錄音殊轴,去河邊找鬼。 笑死袒炉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的樊零。 我是一名探鬼主播我磁,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼驻襟!你這毒婦竟也來了夺艰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤沉衣,失蹤者是張志新(化名)和其女友劉穎郁副,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豌习,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡存谎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肥隆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片既荚。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖栋艳,靈堂內(nèi)的尸體忽然破棺而出恰聘,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布晴叨,位于F島的核電站凿宾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏兼蕊。R本人自食惡果不足惜初厚,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遍略。 院中可真熱鬧惧所,春花似錦、人聲如沸绪杏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕾久。三九已至势似,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間僧著,已是汗流浹背履因。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盹愚,地道東北人栅迄。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像皆怕,于是被迫代替她去往敵國和親毅舆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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