在c語言中,使用以空字符結(jié)尾的字符數(shù)組來表示字符串。Redis在此基礎(chǔ)上定義了一種名為簡單動態(tài)字符串(Simple Dynamic String,SDS) 來表示字符串。
SDS定義
struct sdshdr{
// 記錄buf數(shù)組中已經(jīng)使用的字節(jié)數(shù)量即保存的字符串的長度
int len;
// 記錄buf數(shù)組總還未使用的字節(jié)數(shù)量
int free;
// 字節(jié)數(shù)組 用于保存字符串 這個數(shù)組不保存字符而是保存二進制數(shù)據(jù)
char buf[];
}
在buf數(shù)組中,也保留了一個空字符用作結(jié)尾,該字符不計入SDS的長度中。
C語言使用長度為n+1的字符數(shù)組來表示長度為n的字符串,字符數(shù)組最后一個元素總是空字符'\0'。
SDS的優(yōu)點
常數(shù)級復(fù)雜度獲取字符串的長度
在SDS中l(wèi)en字段用于記錄字符串的長度,因此獲取字符串的長度的操作時間復(fù)雜度為O(1)。C語言中獲取一個字符串的長度時間復(fù)雜度為O(n),需要通過遍歷字符數(shù)組才能獲得。
設(shè)置和更新SDS的長度是由SDS的API在執(zhí)行時自動完成的,在使用SDS過程中不需要手動去修改長度。因此獲取字符串長度這種操作不會成為Redis的性能瓶頸。即使反復(fù)去獲取一個特別長的字符串的長度,時間復(fù)雜度為O(1),不會對系統(tǒng)性能造成任何影響。
避免緩沖區(qū)溢出
SDS的空間分配策略杜絕了發(fā)生緩沖區(qū)溢出的可能性。當(dāng)SDS的API在對buf數(shù)組進行修改時,會先檢查SDS的空間是否滿足修改所需要的要求,如果不滿足,則會自動擴展空間,然后執(zhí)行修改操作。
減少頻繁修改字符串帶來的內(nèi)存重分配次數(shù)
在C語言中,每次修改字符串都會對字符數(shù)組進行一次內(nèi)存重分配操作。內(nèi)存重分配涉及復(fù)雜的算法和系統(tǒng)調(diào)用,是一種比較耗性能的操作。Redis通過內(nèi)存分配策略來避免了頻繁修改字符串帶來的性能損耗。
空間預(yù)分配
空間預(yù)分配用于優(yōu)化字符串增長操作,當(dāng)對字符串進行修改時并且需要擴展空間時,不僅會分配修改所必需的空間,還會分配額外的未使用空間。
當(dāng)修改完SDS后,如果字符串的長度小于1MB,程序會分配和len長度相同的未使用空間,此時len和free值相同。
當(dāng)字符串的長度大于等于1MB時,程序會分配1MB的未使用空間。
空間預(yù)分配策略可以減少連續(xù)增長字符串所需要的內(nèi)存重分配次數(shù),將所需的內(nèi)存重分配次數(shù)從必須N次降低為最多N次。
惰性釋放空間
惰性釋放空間用于優(yōu)化字符串的縮短操作,當(dāng)需要縮短字符串時,不會立即回收縮短后多出來的字節(jié),而是用free記錄起來,方便后續(xù)使用。
二進制安全
C語言的字符串不能包含空字符,因此只能保存文本數(shù)據(jù),不能保存圖片、音頻等二進制數(shù)據(jù)。Redis為了適用各種場景,SDS的API都是以處理二進制的方式處理SDS存放在buf數(shù)組的數(shù)據(jù)。因此數(shù)據(jù)寫入時和讀取時是一樣的。buf數(shù)組用來保存二進制數(shù)據(jù)而不是字符。SDS通過len的值來判斷字符串是否結(jié)束。
兼容部分C中的函數(shù)
SDS遵循C語言字符串以空字符結(jié)尾的慣例,因此可以使用部分C中的函數(shù),從而避免了不必要的代碼重復(fù)。