1:SDS介紹
我們在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大小的冗余空間,
4:兼容c語言函式庫 (字串后面會自動加上\0)
3.2版本以后的SDS結構
前面的len和free以及char這種結構看起來很好,但是是存在一定的問題的
struct sdshdr{
int len;
int free;
char buf[];
}
len和free都是int型別,都是4byte也就是32bit,能表示42億左右的范圍,大大的造成了空間的浪費,所以在3.2以后對SDS有一定的更改,更改如下
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的記憶體分配如圖

當需要存盤的資料長度超過31,就需要用sdshdr8來表示
sdshdr8的記憶體分配如圖

其余的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
}
關注我的技術公眾號,每周都有優質技術文章推送,
微信掃一掃下方二維碼即可關注:
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/172666.html
標籤:其它
下一篇:redis原始碼之dict

