碼神營地-redis數據結構-string

redis數據結構-string
redis最常見數據結構之一就是string,當然這不是簡單的C字符串,它是一種簡單動態字符串,在redis中這種數據類型既能包含C字符串的功能同時又能保持redis的高性能。
更過關於redis操作和學習教程請進入碼神營地官網:www.icodegod.com

  • 數據結構
    redis中的string數據結構是一個結構體,在redis 2.8中是這麼定義的:

    圖1 sds結構體

    其中len就是對應buff數組中的實際字符串長度也即已使用空間,free就是該buff數組剩餘空間大小即未分配空間,單位都是字節,對應的數據結構圖如下:
    圖2  sds結構圖
    從圖可以看到buf中還是遵從C語言字符串風格,使用’\0’來標誌字符串結束,但是這個結束符卻不算做該字符串對象總長度,len標誌了buf的字符串真是長度,這樣做避免計算buf字符串長度進行遍歷操作,可以很方便的得知該字符串對象長度。

    在redis 4新版本中是這麼定義的:
    這裏寫圖片描述

    這裏區分了sds類型,按照具體的string長度來精確劃分使用不同的結構體類型,目的只有一個,那就是爲了節省內存空間。比如裏面使用的__attribute__ ((packed)),用來告知編譯器取消編譯器默認內存對齊方式,這裏的 packed 是一字節對齊,當然這個使用__attribute__ 是GCC特有語法,GCC編譯器是非緊湊模式內存對齊的,此處一字節就成爲了緊湊內存對齊了,當然也都是爲了速度考慮。
    可以看到該結構體中的幾個組成部分:len表示已使用長度,alloc表示總空間大小,在sds.c中還計算了aviallen的大小,這個大小等於alloc-len,flag標誌類型,該部分佔一個字節,但是隻有3個位使用,因爲5種類型,3bytes就可以搞定,剩餘5bytes是未使用,最後buf就是一個可變長動態數組,用於存儲字符串。當然內部還有支持該buf擴容的api函數,以及一系列字符串操作函數。

  • 空間分配
    從sds結構體中可以看到有一個buf的指針,指向一個字符串數組,當我們進行redis字符串對象的拷貝,拼接等操作時,可能當前的數組大小並不能滿足需要,那麼此時就需要考慮使用sds api來實現動態空間分配,這個分配規則如下:

1.如果修改後的sds結構體中的len大小< 1 MB,那麼空閒空間free分配大小則爲len大小。
如果len是5 bytes,free爲 0 bytes,想要拼接一個字符串’hello’,那麼此時還需要5字節的額外空間來滿足需要,那麼擴容前整個sds總共大小爲:5+0+1=6 bytes,因爲buf中還有一字節的’\0’,採用小容量擴容方案擴容後大小爲:10+10+1 = 21 bytes。如圖:

這裏寫圖片描述

2.若修改後len > 1 MB,那麼空閒空間free則爲1 MB。
在sds.h中可以看到有一個宏定義:

#define SDS_MAX_PREALLOC (1024*1024)

該宏定義定義了最大預分配空間大小爲1MB。

  • 空間釋放
    sds釋放空間和大多數經典的數據結構釋放方式一樣,採用的是惰性釋放,buf中釋放的空間會放在free中用作下次使用,就像STL中的內存池一樣,STL中內存池維持着一個16個大小的鏈表,有空間釋放時就回收放到內存池空閒空間,用作後續空間分配。如將上述中的’hello’字符串刪除,那麼空間結構變化如下:

    這裏寫圖片描述

  • 與C語言字符串的比較
    #####1.獲取字符串長度

    1).redis的獲取字符串方式:因爲sds有一個len字段記錄了該字符串的長度,所以很容易以O(1)時間複雜度獲取到。

    這裏寫圖片描述
    2).而C語言字符串則需要使用strlen函數來遍歷字符串計算長度,這樣的時間複雜度就是O(n),相比較而言,redis的效率會高很多。

    #####2.緩衝區安全
    1).redis在進行字符串拼接,拷貝等操作之前會先檢查剩餘空間大小是否能滿足需要,如果不能滿足需要則會使用擴容規則來進行擴容,這樣不會因爲字符串操作空間不足而導致緩衝區溢出,產生安全隱患。
    2).C語言的字符串操作函數進行拷貝和拼接時沒有進行安全檢測,判斷空間是否滿足,這樣操作可能會導致緩衝區溢出。

    #####3.字符串操作導致內存分配
    1).redis中修改字符串長度比如進行字符串縮減,使用安全的api來進行內存預估,從而減少內存分配次數。
    2).C語言中字符串在底層實現是一個字符串數組,如果想在該數組中進行刪減、替換、填充等操作,有N次操作就會有N次內存重新分配,因爲字符串數組必須是連續的。
    #####4.數據類型
    1).redis的sds的buf可以存儲文本和二進制數據。
    2).C語言的字符串只能存儲文本。

    #####5.庫函數
    1).redis只可以使用C語言string.h庫函數中的一部分函數。
    2).C語言中的庫函數都可以用。

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