redis---sds(簡單動態字符串)詳解

1,sds定義

    sds是simple dynamic string的縮寫,意爲簡單動態字符串。

    定義爲:

struct sdshdr {
    long len;         //記錄buf數組已使用的字節數量,即sds保存的字符串的長度,不含'\0'
    long free;        //記錄buf數組中未使用字節的數量
    char buf[0];      //字節數組,用於保存字符串
};

   如圖:

  len:4,表示這個數組保存的字符串的長度是4。

  free:4,表示這個數組的未使用空間是4。

  buf:是一個字符數組,前4個字節保存的是字符'L'、‘u’、‘o’、‘L’,最後一個字節保存的是字符串結束標識‘\0’。

  sds遵循C字符串以空字符結尾的慣例,保存空字符的1字節不計算在sds的len中,並且爲空字符分配額外的1字節空間,以及添  加空字符到字符串末尾等操作,是由sds函數自動完成的。這樣遵循空字符結尾這一慣例的好處是,sds可以直接重用一部分c字符串函數庫裏面的函數。

2,sds與c字符串的區別

     既然sds中保存的也是字符串,爲什麼不直接使用字符串,而還要封裝成一個結構體呢?

     sds的三點優勢:

  • 常數複雜度獲取字符串的長度,通過len屬性獲取長度,複雜度是O(1),字符串循環判斷,是O(n)。
  • 杜絕緩衝區溢出。
  • 減少修改字符串時帶來的內存分配次數。

3,杜絕緩衝區溢出

     C字符串不記錄自身長度帶來的另一問題就是容易造成緩衝區溢出。而sds API需要對sds進行修改時,API會先檢查sds的空間是否滿足修改要求,如果不滿足的話,API會自動將sds的空間擴展至需要的大小,然後執行下面的動作。

4,減少修改字符串時帶來的內存分配次數

     由於C字符串不記錄長度,所以對於一個包含N個字符的C字符串來說,這個字符串的底層實現總是一個N+1個字符長的數組,所以每次增長或縮短一個C字符串,都要對這個數組進行一次內存重分配。

    而sds中,buf數組的長度不一定是字符數量加一,數組裏面可以包含未使用的字節,通過free 屬性,實現了空間預分配和惰性空間釋放兩種優化策略。

   1) 空間預分配。

         空間預分配用於優化sds的字符串增長操作:當sds的API 對一個sds 進行修改,並且需要對sds進行空間擴展的時候,不僅會爲sds 分配修改所必須的空間,還會爲sds 分配額外的未使用空間。

       其中,額外分配的未使用空間數量由以下公式決定:

      A,如果對sds 進行修改後,sds的長度(len值)將小於1MB,那麼分配和len 同樣大小的未使用空間,這時sds 的len和free 的值相同。比如,進行修改後,sds 的len 將變成7字節,那麼也會分配7字節的未使用空間,sds 的buf數組的實際長度將變成

7+ 7 + 1字節(額外的1字節保存空字符)。

     B,如果對sds 進行修改後,sds的長度(len值)將大於等於1MB,那麼分配1MB的未使用空間,即free的值是1MB。比如。如果修改後,sds 的len將變成30MB,那麼會分配1MB 的未使用空間,sds 的buf數組的實際長度將變成30MB + 1MB + 1字節(額外的1字節保存空字符)。

    2) 惰性空間釋放

          惰性空間釋放用於優化sds 的字符串縮短操作:當sds的API 需要縮短sds 保存的字符串時,程序並不立即使用內存分配來回收縮短後多出來的字節,而是使用free 屬性將這些字節數量記錄起來,等待將來使用。

5,二進制安全

     A,C字符串中的字符必須符合某種編碼(一般是ASCII編碼),並且除了字符串的末尾外,字符串裏面不能有空字符。

     B,sds 的API 都是二進制安全的,所有的sds API都會以處理二進制的方式來處理sds 存放在buf數組裏的數據,不會對數據有限制。即redis 不是用這個數組來保存字符,而是用它來保存一系列二進制數據。因爲sds 使用len屬性的值來而不是空字符來判斷字符串是否結束。

6,總結

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章