SDS(simple dynamic string),簡單動態字元串。s同時它被稱為 Hacking String。hack 的地方就在 sds 保存了字元串的長度以及剩餘空間。sds 的實現在 sds.c 中。 C語言字元串使用長度為n+1的字元數組來表示長度為n的字元串,並且字元數組的最後一個元 ...
SDS(simple dynamic string)
,簡單動態字元串。s同時它被稱為 Hacking String。hack 的地方就在 sds 保存了字元串的長度以及剩餘空間。sds 的實現在 sds.c
中。
C語言字元串使用長度為n+1的字元數組來表示長度為n的字元串,並且字元數組的最後一個元素總是空字元'\0',這樣的方式存儲,時存在安全隱患的,並且它不能滿足效率方面的需求。
因此Redis沒有使用C原生的string
而是自己構建了SDS
。在Redis里,C語言字元串只用於一些無須對字元串值進行修改的地方,比如:日誌。
在Redis中,包含字元串值的鍵值對都是使用SDS實現的,除此之外,SDS還被用於AOF緩衝區、客戶端狀態的輸入緩衝區。
SDS定義
struct sdshdr{
//位元組數組
char buf[];
//buf數組中已使用位元組數量
int len;
//buf數組中未使用位元組數量
int free;
}
如上圖所示,len表示該SDS保存了一個6位元組長度(不包含結束符)的字元串,free表示該SDS還有6個位元組的未使用空間,buf是一個char類型的數組
,保存了該SDS所存儲的字元串值。
高效
相比C語言字元串,使獲取字元串長度時間複雜度降為O(1)
而C原生的獲取長度為O(N)
遍歷整個數組。
安全
同時SDS
杜絕緩衝區溢出,不會像C那樣造成數組數據不安全,絕對不會越界。
當需要對SDS進行修改時,API會先檢查SDS當前剩餘空間是否滿足修改之後所需的空間,如果不滿足的話API會自動將SDS的空間擴展至足夠用的空間然後才進行下一步操作,所以SDS不會出現緩衝區溢出問題。
減少記憶體分配
C語言字原生符串底層是使用一個n+1個字元長度的char類型數據實現的,所以每次增長或縮短一個原生字元串,程式都要對這個字元串數組進行一次記憶體重分配操作:
同時因為記憶體重分配涉及複雜的演算法,並且可能需要執行系統調用,所以它通常是一個比較耗時的操作。Redis經常被用於速度要求嚴苛、數據被頻繁修改的場合,如果每次修改字元串都需要執行一次記憶體重分配的話,那麼對於性能會造成很大影響。
SDS 在分配了記憶體之後(往往空間會存在盈餘,也就是空間的預分配),然後自己通過len 和 free 來維護已使用的和未使用的記憶體,不再依賴系統來重新劃分,這樣能有效的提升性能。
空間預分配
用於字元串增長操作,當字元串增長時,程式會先檢查需不需要對SDS空間進行擴展,如果需要擴展,程式不僅會為SDS分配修改所必要的空間,還會為SDS分配額外的未使用空間,額外分配的未使用空間公式如下:
SDS空間 < 1MB
如果對SDS修改之後,SDS的長度(修改之後len屬性的值)小於1MB,那麼則分配和len屬性同樣大小的未使用空間,這時SDS的len屬性和free屬性的值相同。如:如果修改之後SDS的len將變為10位元組,那麼程式也會分配10位元組的未使用空間,SDS的buf數組實際長度變為10 + 10 + 1 = 21(額外一個位元組用於保存結束符\n)
SDS空間 > 1MB
如果對SDS修改之後,SDS的長度大於等於1MB,那麼程式會分配1MB的未使用空間。如:修改之後的len將變為10MB,那麼程式會分配1MB的未使用空間,SDS的bug數組長度為10MB + 1MB + 1byte
SDS空間 > 512MB
Game over~ 報錯!
惰性空間釋放
用於優化SDS的字元串收縮操作,當字元串收縮時,程式不會立即執行記憶體重分配來回收收縮後記憶體多出來的空間,而是使用free屬性記錄下來,以備將來使用。
通過空間預分配,Redis可以減少連續執行字元串增長操作所需的記憶體重分配次數,通過惰性空間釋放,SDS避免了縮短字元串時所需的記憶體重分配操作,併為將來由可能的增長操作提供了優化。