|
C語(yǔ)言字符串使用長(zhǎng)度為n+1的字符數(shù)組來(lái)表示長(zhǎng)度為n的字符串,并且字符數(shù)組的最后一個(gè)元素總是空字符'\0',這樣的方式存儲(chǔ),時(shí)存在安全隱患的,并且它不能滿足效率方面的需求。 因此Redis沒(méi)有使用C原生的 在Redis中,包含字符串值的鍵值對(duì)都是使用SDS實(shí)現(xiàn)的,除此之外,SDS還被用于AOF緩沖區(qū)、客戶端狀態(tài)的輸入緩沖區(qū)。 SDS定義
如上圖所示,len表示該SDS保存了一個(gè)6字節(jié)長(zhǎng)度(不包含結(jié)束符)的字符串,free表示該SDS還有6個(gè)字節(jié)的未使用空間,buf是一個(gè)char類型的 高效相比C語(yǔ)言字符串,使獲取字符串長(zhǎng)度時(shí)間復(fù)雜度降為 安全同時(shí) 當(dāng)需要對(duì)SDS進(jìn)行修改時(shí),API會(huì)先檢查SDS當(dāng)前剩余空間是否滿足修改之后所需的空間,如果不滿足的話API會(huì)自動(dòng)將SDS的空間擴(kuò)展至足夠用的空間然后才進(jìn)行下一步操作,所以SDS不會(huì)出現(xiàn)緩沖區(qū)溢出問(wèn)題。 減少內(nèi)存分配C語(yǔ)言字原生符串底層是使用一個(gè)n+1個(gè)字符長(zhǎng)度的char類型數(shù)據(jù)實(shí)現(xiàn)的,所以每次增長(zhǎng)或縮短一個(gè)原生字符串,程序都要對(duì)這個(gè)字符串?dāng)?shù)組進(jìn)行一次內(nèi)存重分配操作: 同時(shí)因?yàn)閮?nèi)存重分配涉及復(fù)雜的算法,并且可能需要執(zhí)行系統(tǒng)調(diào)用,所以它通常是一個(gè)比較耗時(shí)的操作。Redis經(jīng)常被用于速度要求嚴(yán)苛、數(shù)據(jù)被頻繁修改的場(chǎng)合,如果每次修改字符串都需要執(zhí)行一次內(nèi)存重分配的話,那么對(duì)于性能會(huì)造成很大影響。 SDS 在分配了內(nèi)存之后(往往空間會(huì)存在盈余,也就是空間的預(yù)分配),然后自己通過(guò)len 和 free 來(lái)維護(hù)已使用的和未使用的內(nèi)存,不再依賴系統(tǒng)來(lái)重新劃分,這樣能有效的提升性能。 空間預(yù)分配用于字符串增長(zhǎng)操作,當(dāng)字符串增長(zhǎng)時(shí),程序會(huì)先檢查需不需要對(duì)SDS空間進(jìn)行擴(kuò)展,如果需要擴(kuò)展,程序不僅會(huì)為SDS分配修改所必要的空間,還會(huì)為SDS分配額外的未使用空間,額外分配的未使用空間公式如下: SDS空間 < 1MB 如果對(duì)SDS修改之后,SDS的長(zhǎng)度(修改之后len屬性的值)小于1MB,那么則分配和len屬性同樣大小的未使用空間,這時(shí)SDS的len屬性和free屬性的值相同。如:如果修改之后SDS的len將變?yōu)?0字節(jié),那么程序也會(huì)分配10字節(jié)的未使用空間,SDS的buf數(shù)組實(shí)際長(zhǎng)度變?yōu)?0 + 10 + 1 = 21(額外一個(gè)字節(jié)用于保存結(jié)束符\n) SDS空間 > 1MB 如果對(duì)SDS修改之后,SDS的長(zhǎng)度大于等于1MB,那么程序會(huì)分配1MB的未使用空間。如:修改之后的len將變?yōu)?0MB,那么程序會(huì)分配1MB的未使用空間,SDS的bug數(shù)組長(zhǎng)度為10MB + 1MB + 1byte SDS空間 > 512MB
惰性空間釋放用于優(yōu)化SDS的字符串收縮操作,當(dāng)字符串收縮時(shí),程序不會(huì)立即執(zhí)行內(nèi)存重分配來(lái)回收收縮后內(nèi)存多出來(lái)的空間,而是使用free屬性記錄下來(lái),以備將來(lái)使用。 通過(guò)空間預(yù)分配,Redis可以減少連續(xù)執(zhí)行字符串增長(zhǎng)操作所需的內(nèi)存重分配次數(shù),通過(guò)惰性空間釋放,SDS避免了縮短字符串時(shí)所需的內(nèi)存重分配操作,并為將來(lái)由可能的增長(zhǎng)操作提供了優(yōu)化。 |
|
|