Redis 數據結構之String

目的

Redis如今是各個系統幾乎都在使用的一種分佈式高可用的緩存內存中的數據結構存儲系統。能夠做爲數據庫、緩存消息中間件、訂閱發佈系統等。咱們都知道redis中有string、sets、sorted sets、hash、list類型。可是這些咱們常用的數據結構的底層是怎麼實現的。今天先記錄一下string的結構。主要是參照Redis設計與實現和一些網上的資料總結的一個學習筆記。redis

C語言字符串和SDS在Redis中的使用

redis是用C語言實現的,因此在redis中的string有一些是直接使用C語言的字符串。可是在redis中還使用了叫作簡單動態字符串(simple dynamic string)。redis中的string主要就是使用這兩種string。可是何時用哪一種呢?具體咱們來看一下。
C語言string:主要使用在一些無序對字符串進行修改的地方,好比說做爲key、打印日誌
SDS:SDS是redis中默認字符串表示。幾乎用於全部須要使用字符串操做的地方,包括AOF緩衝區等模塊。
咱們舉一個例子:數據庫

redis> set aaa "bbb"
這個時候aaa使用的是C語言字符串,bbb則是使用SDS
RPUSH list "aaa" "bbb" "ccc"
list這個key使用的是C語言字符串,aaa bbb ccc則是使用了3個SDS來保存

SDS定義

SDS的定義其實很簡單,一共有3個屬性。len、free、buf數組

struct sdshdr{
    //記錄buf數組中已經使用的數量
    //等於整個字符串目前已經使用的長度(不包括結束字符"\0")
    int len;
    //記錄buf數組中未使用的字節數量
    int free;
    //字節數組,用於保存字符串的地方
    char buf[]
}

咱們能夠看到相比C語言中的字符串,SDS多了兩個屬性len和free。但就是這兩個簡單的屬性再加上一些擴容策略就能夠是的性能有一個很大的提高。接下來咱們能夠看一下SDS的優點點在哪裏。緩存

SDS優點點

  1. 快速獲取字符串的長度
    在SDS的結構中有一個len的屬性,若是要獲取字符串長度則直接返屬性便可。若是C語言字符串,則須要循環字節數組遇到了"0"計算出整個字符串的長度。很明顯能夠看到SDS使用的複雜度是O(1)。因此說在redis中獲取字符串的長度對性能幾乎是沒有影響的。
  2. 杜絕緩衝區溢出
    在C語言字符串中,當你添加字符到一個字符串是s1長度時若是忘記在執行前爲s1分配足夠多的空間,那麼s1的數據將溢出到以後的字符串s2中,致使s2被意外的修改。
    然而SDS不一樣,每次對SDS字符串修改以前都會去判斷字符串的容量是否足夠。若是不夠則會擴充SDS的內存大小。因此徹底避免了這個錯誤。
  3. 減小字符串修改帶來的內存分配次數
    在C語言字符串中,每次對字符串的修改都會影響到內存的分配。若是增加字符串,則會爲字符串從新分配一個新的內存空間。若是減小字符串,則會對減小的內存空間作內存回收,不然會引發內存泄漏。
    那麼SDS是如何減小內存分配的呢? 是經過兩點
  • 空間的預分配
    當SDS的API對一個SDS進行修改,而且須要擴展空間的時候,不只會對SDS分配所須要的空間,還會爲SDS分配額外的未使用空間。這個未使用空間的長度則記錄在free屬性中。這個預分配空間的策略根據SDS的長度決定。
    1 當SDS小於1MB的時候。每次擴容長度則跟len相同。舉一個例子若是一個SDS擴容爲12個字節,那麼 SDS函數將會再添加一個12字節的預分配長度。既 len=12 free=12 buf長度則爲25字節 (多出來的1個是結束符"0")
    2 當SDS大於1MB的時候,每次預分配長度則爲1MB。至關於若是這個SDS是10MB,那麼每次擴容以後的free的長度回是1MB
  • 惰性空間釋放
    當一個SDS字符串縮短操做時,程序並不會立刻從新收回多餘出來的內容,而是用free字段將這些空餘的空間記錄下來。當下次若是須要往SDS添加字符,則能夠再次使用這些空餘空間。固然也不是意味着永遠不會被回收,SDS有API來釋放這些內存空間。
  1. 二進制安全
    開始沒有理解什麼事二進制安全,網上找了一些資料。簡單總結:**二進制安全的意思就是,只關心二進制化的字符串,不關心具體格式,只會嚴格的按照二進制的數據存取,不會妄圖以某種特殊格式解析數據。
    C語言字符串的字符必須符合某種編碼(好比ASCII)而且不能包含空字符,不然空字符以後的字符將會被忽略。二SDS則都是使用二進制處理,不只能夠存放字符串還能夠放字節流,好比圖片,視頻等。由於redis不使用空字符串來判斷長度而是用len屬性。

總結

由於redis做爲一個數據庫存儲來使,並且redis是一個單線程,一旦一個操做阻塞了以後以後的全部操做都會被影響。因此對性能的要求特別高,因此redis本身構建了這種字符串。 其實SDS結構十分簡單,簡單的使用了len,free兩個字段配合一些策略,就大幅度的提升了性能和減小了內存從新分配的次數。值得咱們學習,能夠應用在其餘程序設計中。安全

相關文章
相關標籤/搜索