Redis資料類型之字串,redis資料類型
Redis資料類型之字串redis的字串
redis的字串不是C語言原生的字串,而是自己構建的稱為簡單動態字串(simple dynamic string),簡稱 SDS,和C語言原生的字串相似,使用’\0’作為結尾。
除了列印日誌之外,我們操作字串基本是在使用SDS
SDS的在redis的主要功能
1. 儲存資料庫的字串值 2. 用作緩衝區buffer
SDS在redis的定義
在源碼包下面的src目錄下的sds.h 和sds.c
typedef char *sds; /** * 儲存字串對象的結構 */ struct sdshdr { // buf 中已佔用空間的長度 int len; // buf 中剩餘可用空間的長度 int free; // 資料空間 char buf[]; };
為什麼使用SDS而不是C語言原生的字串?
SDS有一下特點
/** * 返回實際的長度 */ static inline size_t sdslen(const sds s) { struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); return sh->len; }
strcat(char* dest,const char* src)
看看sds.c中的sdscat()
//函數原型 sds sdscat(sds s, const char *t); //具體實現 sds sdscat(sds s, const char *t) { return sdscatlen(s, t, strlen(t)); } //sdscatlen sds sdscatlen(sds s, const void *t, size_t len) { struct sdshdr *sh; // 原有字串長度 size_t curlen = sdslen(s); // 擴充 sds 空間 // T = O(N) s = sdsMakeRoomFor(s,len); // 記憶體不足?直接返回 if (s == NULL) return NULL; // 複製 t 中的內容到字串後部 // T = O(N) sh = (void*) (s-(sizeof(struct sdshdr))); memcpy(s+curlen, t, len); // 更新屬性 sh->len = curlen+len; sh->free = sh->free-len; // 添加新結尾符號 s[curlen+len] = '\0'; // 返回新 sds return s; }
可以看到,sds在進行拼接的時候,首先擷取原字串的長度,然後進行擴充。
3. 減少修改字串時的記憶體重新分配
C語言的字串的首先還是依靠字元數組,長度為N的字串對應的是一個長度為N+1的字元數組,所以,在對字串進行操作時,都會發生對字元數組的記憶體重新分配。
對於記憶體的重新分配,一般設計複雜的演算法和系統調用,相對比較耗時,偶爾一次的重新分配還可以接受,但是對於redis,頻繁的操作,肯定效率非常低下。
還記得sds的結構體嗎?儲存了一個未使用的空間。sds通過這個來屏蔽了與底層數組的關聯 。
那麼sds如何使用這個free來實現減少記憶體重新分配?
方法一、記憶體預分配。 最佳化sds的增長操作
簡單的說,就是redis API 在修改sds的時候,如果有涉及對sds長度進行擴充,那麼,同時擴充sds的free空間長度。如果free足夠,那麼就使用free空間。
那麼擴充多少?free與length又有什麼大小關係?
- 如果擴充之後的sds小於1M,那麼,擴充之後的free==length,總的記憶體大小是 free+length+1byte
- 如果擴充之後的sds大于于1M,那麼,free的大小是1M,總長度是1M+length+1byte
方法二、惰性空間釋放 最佳化sds的縮短操作
簡單的說,就是redis API 在截斷sds的時候,程式並不立即回收多出來的記憶體,而是使用free屬性來儲存這些空間。
實現代碼
sds sdstrim(sds s, const char *cset) { struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); char *start, *end, *sp, *ep; size_t len; // 設定和記錄指標 sp = start = s; ep = end = s+sdslen(s)-1; // 修剪, T = O(N^2) while(sp <= end && strchr(cset, *sp)) sp++; while(ep > start && strchr(cset, *ep)) ep--; // 計算 trim 完畢之後剩餘的字串長度 len = (sp > ep) ? 0 : ((ep-sp)+1); // 如果有需要,前移字串內容 // T = O(N) if (sh->buf != sp) memmove(sh->buf, sp, len); // 添加終結符 sh->buf[len] = '\0'; // 更新屬性 sh->free = sh->free+(sh->len-len); sh->len = len; // 返回修剪後的 sds return s;}
二進位安全
C語言的字串只能用來儲存ANSI字元,因為不能包含Null 字元。
所有的sds的api都會以處理二進位的方式來處理儲存在sds位元組數組的資料,不對這些資料進行任何的處理。
相容部分c語言的字串。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。