Redis原始碼閱讀:Redis字串SDS

Npcccccc發表於2018-06-21

SDS 基本概念

簡單動態字串(Simple Dynamic String)SDS,用作Redis 的預設字串。
C語言中的字串:以空字元結尾的字元陣列

SDS實現舉例

redis > SET msg "hello world"
OK

我們通過 SETRedis 資料庫中建立了一個資料鍵物件為 "msg" 和 資料值物件為 "hello world" 的鍵值對,其中資料鍵和資料值物件底層的字串實現都是 SDS 。同時, SDS 還被用於 AOF 緩衝區。

SDS 定義

struct sdshdr {

    # 記錄 buf 陣列中已使用位元組的數量,即當前字串長度值  

    # 等於 SDS 所儲存字串的位元組長度

    int len;

    # 記錄 buf 陣列中未使用位元組的數量,buf空餘可用的長度,append時使用  

    int free;

    # 位元組char陣列,用於儲存字串,實際儲存字串資料,最後一個位元組儲存了空字元 '\0'

    char buf[];
};

buf 屬性的位元組陣列中的字串長度等於 len 屬性值加上1,因為 Redis遵循 C語言的規範,在SDS資料型別字串的結尾加上了 空字串,額外佔用 1 個位元組空間,這1個位元組空間不計算在 SDS 的 len屬性裡面。

由於SDS將字串的結尾加上了 空字串符合C語言字串規範,Redis 字串操作可以相容C語言中一部分字串庫中的函式,Redis 無需專門為 SDS在編寫一套函式。

SDS的優點

常數複雜度獲取字串長度

  1. C字串需要遍歷整個字串,計數,直到碰到空字元,停止計數,複雜度為O(N)

  2. SDS獲取 len 屬性值即可,複雜度為 O(1) 。所以 STRLEN 的複雜度也為 O(1)

API安全,杜絕緩衝區溢位

  1. C字串在進行字串拼接 strcat 時,需要預先分配足夠的空間,來容納拼接的字串,否則會造成緩衝區溢位的問題,比如臨近的空間有另外一個字串。

  2. SDS 在進行字串拼接時,會先檢查 len 的長度是否足夠,如果不夠,會先擴充套件 len,再進行字串拼接。

減少修改字串長度時所需的記憶體重分配次數

  1. 空間預分配
    當對 SDS 進行空間擴充套件時,計算擴充套件之後的 len值如果小於 1mb,那麼久會分配 擴充套件之後的 len 值給 free 屬性作為,為下次擴充套件時預分配的未使用空間,如果下次擴充套件所需位元組空間小於 free 的值,那麼就無需進行空間擴充套件,直接使用未使用空間。

  2. 惰性空間釋放
    同樣,預設情況下,對 SDS 進行縮減時,縮減的空間不會立刻被這個SDS釋放,而是分配給 free ,如果之後再進行擴充套件時,有可能會用到。

Redis 的 SDS 型別通過這兩種空間分配策略,減少了字串增長縮減時所需的記憶體重分配操作,為記憶體分配提供了優化。

二進位制安全

Redis 通過 len屬性的值來判斷是否結束,而不是C字串的 \0 作為結束。

相容部分C字串函式

上面已經提到SDS在末尾新增了 \0 ,這樣可以相容部分C字串函式,可以直接使用 <string.h> 函式庫。

相關文章