1、什麼是SDS?
Redis 自定的字串儲存結構,關於redis,你需要了解的幾點!中我們對此有過簡要說明。
Redis 底層是用C語言編寫的,可是在字元儲存上,並未使用C原生的String型別,而是定義了自己的字串結構 Simple Dynamic Stirng,簡稱SDS。
SDS基本結構如下:
struct sdshdr {
int len; // 記錄buf陣列中已使用位元組的數量,等於SDS所儲存字串的長度
int free; // 記錄buf陣列中未使用位元組的數量
char buf[];// 位元組陣列,用於儲存字串
};
例如,字串“Redis”儲存示意圖為:
圖1
當前儲存字串長度為5,未使用長度為0,位元組陣列儲存的字元為“Redis\0”。
這裡需要注意的是:內部資料陣列儲存字串形式符合C語言要求,以‘\0’結尾。且len字串長度不包含結尾識別符號‘\0’。
buf[]的這種遵循C語言形式的儲存,使得Redis可以直接使用C語言的相關字串函式進行SDS物件的操作。
二、SDS的優勢
1、O(1)時間複雜度獲取字串長度
SDS內部維護著一個字串長度的len變數,可以直接讀取,時間複雜度為O(1)。
對於傳統的C字串:字元+“\0”,想要獲取字元長度,則需要遍歷整個字串,直到遇到結束字元,時間複雜度為O(n)。
2、緩衝區溢位規避
所謂緩衝區溢位即所需要的記憶體超出了實際的記憶體。因此對於C字串來說,要特別注意記憶體分配,回收使用問題。
比如,向一個現有字串內新增特定字元時,需要保證當前已經分配了這足夠的記憶體。
圖2
與C不同的是,SDS的空間預分配策略可以避免緩衝區溢位發生,
當需要對SDS進行操作時,首先會檢查當前空間是否滿足需求,不足則擴充套件當前分配空間。記憶體檢查相對於C變成了內部預置操作。
3、減少記憶體重分配次數
上面我們講到過,C字元操作前都需要進行記憶體的分配操作,同時,操作完成後,也需要進行相應的記憶體回收操作。一次操作至少涉及一次記憶體分配操作。
大家都知道記憶體的重分配是一個比較複雜且需精細控制的過程,耗時耗資源。針對此弊端,Redis 在SDS記憶體配置策略上採用了空間預分配+惰性刪除相結合的策略。
a)空間預分配:
空間預分配用於優化SDS字元擴充套件操作。
所謂預分配,也即是說在一次擴充套件操作中,擴充套件的空間大小會大於實際需要的空間大小。
如下,圖1執行圖2操作後SDS變更為:
圖3
預分配空間的大小基於以下規則計算:
SDS len<1M:分配len長度空間作為預分配空間;
SDS len>=1M:分配1M空間作為預分配空間;
這樣,在下次進行字元操作的時候,如果所需要的空間小於當前SDS free空間,則可以直接行操作,而不需要再執行記憶體擴充套件,重分配操作。
SDS的預分配機制使得一次擴充套件操作所需的記憶體重分配次數變為<=1。
b)惰性刪除機制
所謂惰性刪除,即調整刪除SDS中部分資料時,不會立刻執行記憶體重分配,而是會保留空出來記憶體,並更新內部free屬性。以備將來有字元擴充套件需求,可以直接使用。
當然,Redis也提供了主動釋放未使用記憶體的方法。
如下,刪除“ent”之後的SDS結構:
圖4
SDS的記憶體分配機制,尤其對於以寫為主的應用場景,能夠提供更加優異的效能表現。
3、二進位制安全
C字串由於特殊的編碼要求只能儲存文字資料。
SDS相關的功能方法會以二進位制的形式來操作SDS儲存的資料,沒有任何中間操作,儲存最原始的資料,因此不會有字元層面的因素影響。
SDS可以儲存任何源的二進位制資料,字元、圖片、檔案或者序列化的物件等等。