1. 程式人生 > 其它 >Redis的底層資料結構-SDS

Redis的底層資料結構-SDS

 SDS 定義:

struct sdshdr{
     //記錄buf陣列中已使用位元組的數量
     //等於 SDS 儲存字串的長度
     int len;
     //記錄 buf 陣列中未使用位元組的數量
     int free;
     //位元組陣列,用於儲存字串
     char buf[];
}

用SDS儲存字串 “Redis”具體圖示如下:

  

         圖片來源:《Redis設計與實現》

  我們看上面對於 SDS 資料型別的定義:

  1、len 儲存了SDS儲存字串的長度

  2、buf[] 陣列用來儲存字串的每個元素

  3、free j記錄了 buf 陣列中未使用的位元組數量

  上面的定義相對於 C 語言對於字串的定義,多出了 len 屬性以及 free 屬性。為什麼不使用C語言字串實現,而是使用 SDS呢?這樣實現有什麼好處?

  ①、常數複雜度獲取字串長度

  由於 len 屬性的存在,我們獲取 SDS 字串的長度只需要讀取 len 屬性,時間複雜度為 O(1)。而對於 C 語言,獲取字串的長度通常是經過遍歷計數來實現的,時間複雜度為 O(n)。通過 strlen key 命令可以獲取 key 的字串長度。

  ②、杜絕緩衝區溢位

  我們知道在 C 語言中使用 strcat 函式來進行兩個字串的拼接,一旦沒有分配足夠長度的記憶體空間,就會造成緩衝區溢位。而對於 SDS 資料型別,在進行字元修改的時候,會首先根據記錄的 len 屬性檢查記憶體空間是否滿足需求,如果不滿足,會進行相應的空間擴充套件,然後在進行修改操作,所以不會出現緩衝區溢位。

  ③、減少修改字串的記憶體重新分配次數

  C語言由於不記錄字串的長度,所以如果要修改字串,必須要重新分配記憶體(先釋放再申請),因為如果沒有重新分配,字串長度增大時會造成記憶體緩衝區溢位,字串長度減小時會造成記憶體洩露。

  而對於SDS,由於len屬性和free屬性的存在,對於修改字串SDS實現了空間預分配和惰性空間釋放兩種策略:

  1、空間預分配:對字串進行空間擴充套件的時候,擴充套件的記憶體比實際需要的多,這樣可以減少連續執行字串增長操作所需的記憶體重分配次數。

  2、惰性空間釋放:對字串進行縮短操作時,程式不立即使用記憶體重新分配來回收縮短後多餘的位元組,而是使用 free 屬性將這些位元組的數量記錄下來,等待後續使用。(當然SDS也提供了相應的API,當我們有需要時,也可以手動釋放這些未使用的空間。)

  ④、二進位制安全

  因為C字串以空字元作為字串結束的標識,而對於一些二進位制檔案(如圖片等),內容可能包括空字串,因此C字串無法正確存取;而所有 SDS 的API 都是以處理二進位制的方式來處理 buf 裡面的元素,並且 SDS 不是以空字串來判斷是否結束,而是以 len 屬性表示的長度來判斷字串是否結束。

  ⑤、相容部分 C 字串函式

  雖然 SDS 是二進位制安全的,但是一樣遵從每個字串都是以空字串結尾的慣例,這樣可以重用 C 語言庫<string.h> 中的一部分函式。

  ⑥、總結

  

  一般來說,SDS 除了儲存資料庫中的字串值以外,SDS 還可以作為緩衝區(buffer):包括 AOF 模組中的AOF緩衝區以及客戶端狀態中的輸入緩衝區。