redis源碼之SDS

1:SDS介紹

咱們在redis中執行命令redis

set key name

的時候,key和name都是字符串類型,並且字符串(string)在redis中是會常常用到的類型,那redis是如何保存字符串的呢?咱們接下來往下看
衆所周知,redis是c寫的,在c中使用char來保存字符串,而且用\0做爲字符串的結尾,可是redis不是這樣保存的,redis是使用一種叫SDS的結構來保存字符串的。結構以下(redis3.2之前)數據庫

struct sdshdr{
   int len;
   int free;
   char buf[];
}

那麼問題來了,redis爲何 會用SDS的結構,而不直接用c語言的字符串,咱們來看看他們的區別緩存

1:計算字符串長度的區別

對於c來講,計算字符串的長度的方式就是遍歷,遇到\0就中止,因此複雜對是O(n),而SDS直接保存了字符串的長度,複雜度是O(1)安全

2:保證二進制的安全

由於SDS並非以\0爲結尾的標誌,天然就保證了二進制的安全微信

3:內存管理策略(預分配內存和惰性空間釋放策略)

redis是一個高速的緩存數據庫,須要頻繁的對字符串進行操做,若是內存分配錯誤,會致使很嚴重的後果,就算內存分配沒問題,頻繁的內存分配也是很是耗費時間的,因此這些都是應該去避免的函數

惰性空間釋放策略

在SDS中首先用到了惰性空間釋放策略,惰性空間釋放用於優化SDS的字符串縮短操做。
當要縮短SDS保存的字符串時,程序並不當即使用內存充分配來回收縮短後多出來的字節,而是使用表頭的free成員將這些字節記錄起來,並等待未來使用。
源碼以下優化

void sdsclear(sds s) {  //重置sds的buf空間,懶惰釋放
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    sh->free += sh->len;    //表頭free成員+已使用空間的長度len = 新的free
    sh->len = 0;            //已使用空間變爲0
    sh->buf[0] = '\0';         //字符串置空
}
預分配內存

擴容策略是字符串在長度小於 1M 以前,擴容空間採用加倍策略,也就是保留 100% 的冗餘空間。當長度超過1M 以後,爲了不加倍後的冗餘空間過大而致使浪費,每次擴容只會多分配 1M大小的冗餘空間。ui

4:兼容c語言函數庫 (字符串後面會自動加上\0)

3.2版本之後的SDS結構

前面的len和free以及char這種結構看起來很好,可是是存在必定的問題的3d

struct sdshdr{
   int len;
   int free;
   char buf[];
}

len和free都是int類型,都是4byte也就是32bit,能表示42億左右的範圍,大大的形成了空間的浪費,因此在3.2之後對SDS有必定的更改,更改以下code

typedef char *sds;

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
.........

sdshdr5表示的是用5個bit位來表示數據的長度,sdshdr8就是表示用8個bit位來表示數據的長度,以此類推
sdshdr5的內存分配如圖
sdshdr5.png
當須要存儲的數據長度超過31,就須要用sdshdr8來表示
sdshdr8的內存分配如圖
sdshdr8.png

其他的sdshdr16以上的都是以此類推,判斷方式源碼以下

static inline char sdsReqType(size_t string_size) {
    if (string_size < 1<<5) //2^5-1
        return SDS_TYPE_5;
    if (string_size < 1<<8) //2^8-1
        return SDS_TYPE_8;
    if (string_size < 1<<16) //2^16-1
        return SDS_TYPE_16;
#if (LONG_MAX == LLONG_MAX)
    if (string_size < 1ll<<32) //2^32-1
        return SDS_TYPE_32;
    return SDS_TYPE_64;
#else
    return SDS_TYPE_32;
#endif
}

關注個人技術公衆號,每週都有優質技術文章推送。
微信掃一掃下方二維碼便可關注:
file

相關文章
相關標籤/搜索