Redis使用自己的簡單動態(tài)字符串(simple dynamic string, SDS)的抽象類型。Redis中豫缨,默認(rèn)以SDS作為自己的字符串表示缩举。
struct sdshdr {
// 用于記錄buf數(shù)組中使用的字節(jié)的數(shù)目
// 和SDS存儲的字符串的長度相等
int len;
// 用于記錄buf數(shù)組中沒有使用的字節(jié)的數(shù)目
int free;
// 字節(jié)數(shù)組洁奈,用于儲存字符串
char buf[]; //buf的大小等于len+free+1串慰,其中多余的1個字節(jié)是用來存儲’\0’的。
};
SDS除了用來保存數(shù)據(jù)庫中的字符串之外笙各,SDS還被用作緩沖區(qū)(buffer)钉答,如AOF模塊中的AOF緩沖區(qū),以及客戶端狀態(tài)中的輸入緩沖區(qū)
使用SDS而不使用c語言的string的好處:
1杈抢、常數(shù)復(fù)雜度獲取字符串長度
C語言中:字符串只是簡單的字符的數(shù)組数尿,當(dāng)使用strlen獲取字符串長度的時候,內(nèi)部其實是直接順序遍歷數(shù)組的內(nèi)容惶楼,找到對應(yīng)的’\0’對應(yīng)的字符右蹦,從而計算出字符串的長度。即O(N)鲫懒。
SDS:只需要訪問SDS的len屬性就能得到字符串的長度嫩实,復(fù)雜度為O(1)。
2窥岩、杜絕緩沖區(qū)溢出
Redis是C語言編寫的,并沒有方便的數(shù)據(jù)類型來進(jìn)行內(nèi)存的分配和釋放(C++ STL String)宰缤,必須手動進(jìn)行內(nèi)存分配和釋放颂翼。
對于字符串的拼接、復(fù)制等操作慨灭,C語言開發(fā)者必須確保目標(biāo)字符串的空間足夠大朦乏,不然就會出現(xiàn)溢出的情況。
當(dāng)使用SDS的API對字符串進(jìn)行修改的時候氧骤,
API內(nèi)部第一步會檢測字符串的大小是否滿足呻疹。
如果空間已經(jīng)滿足要求,那么就像C語言一樣操作即可筹陵。如果不滿足刽锤,則拓展buf的空間
之后再進(jìn)行操作镊尺。每次操作之后,len和free的值會做相應(yīng)的修改并思。
擴(kuò)展buf空間策略:
修改之后總長度len<1MB: 總空間為2*len+1;
修改之后總長度len>=1MB: 總空間為len+1MB+1庐氮。
換句話說,預(yù)分配的空間上限是1MB宋彼,盡量為len弄砍。
3、減少修改字符串時帶來的內(nèi)存重分配次數(shù)
Redis主要通過以下兩種策略來處理內(nèi)存問題输涕。
字符串長度增加操作時音婶,進(jìn)行空間預(yù)分配
字符串長度減少操作時,惰性空間釋放
當(dāng)執(zhí)行字符串長度縮短的操作的時候莱坎,SDS并不直接重新分配多出來的字節(jié)衣式,而是修改len和free的值(len相應(yīng)減小,free相應(yīng)增大型奥,buf的空間大小不變化)瞳收,避免內(nèi)存重分配。
SDS也提供直接釋放未使用空間的API厢汹,在需要的時候螟深,也能真正的釋放掉多余的空間。
4烫葬、二進(jìn)制安全
C字符串除了末尾之外不能出現(xiàn)空字符界弧,否則會被程序認(rèn)為是字符串的結(jié)尾。這就使得C字符串只能存儲文本數(shù)據(jù)搭综,而不能保存圖像垢箕,音頻等二進(jìn)制數(shù)據(jù)。
使用SDS就不需要依賴控制符兑巾,而是用len來指定存儲數(shù)據(jù)的大小条获,所有的SDS API都會以處理二進(jìn)制的方式來處理SDS的buf的數(shù)據(jù)。程序不會對buf的數(shù)據(jù)做任何限制蒋歌、過濾或假設(shè)帅掘,數(shù)據(jù)寫入的時候是什么,讀取的時候依然不變堂油。
5修档、兼容部分C字符串函數(shù)
SDS的buf的定義(字符串末尾為’\0’)和C字符串完全相同,因此很多的C字符串的操作都是適用于SDS->buf的府框。比如當(dāng)buf里面存的是文本字符串的時候吱窝,大多數(shù)通過調(diào)用C語言的函數(shù)就可以。
二、總結(jié)
C字符串 SDS
獲取字符串長度的復(fù)雜度為O(N) 獲取字符串長度的復(fù)雜度為O(1)
API是不安全的院峡,可能會造成緩沖區(qū)溢出 API是安全的兴使,不會造成緩沖區(qū)溢出
修改字符串長度N次必然需要執(zhí)行N次內(nèi)存重分配 修改字符串長度N次最多需要執(zhí)行N次內(nèi)存重分配
只能保存文本數(shù)據(jù) 可以保存文本或者二進(jìn)制數(shù)據(jù)
可以使用所有庫中的函數(shù) 可以使用一部分庫的函數(shù)