Redis SDS 深入一點,看到更多!

WindWant發表於2020-06-24

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可以儲存任何源的二進位制資料,字元、圖片、檔案或者序列化的物件等等。

 

相關文章