傳統(tǒng)的C實(shí)現(xiàn)的字符串char*的不足之處好芭?
- 1燃箭、以'\0'為字符串結(jié)尾,無法實(shí)現(xiàn)任意的字符串的保存舍败,會(huì)被截?cái)唷?/li>
- 2招狸、無法進(jìn)行高效的操作,例如獲取字符串長(zhǎng)度邻薯、比較裙戏、追加等操作。
- 3厕诡、無法保存二進(jìn)制類型累榜。
- 4、操作復(fù)雜灵嫌,追加壹罚,創(chuàng)建等操作涉及到內(nèi)存的分配冀偶。
redis設(shè)計(jì)的SDS的優(yōu)點(diǎn)?
- 1渔嚷、高效的操作进鸠。redis以高效為名,高效的數(shù)據(jù)結(jié)構(gòu)是保障形病。設(shè)計(jì)高效的字符串操作的數(shù)據(jù)接口客年。
- 2、保存任意字符串漠吻。C中的字符串實(shí)現(xiàn)以'/0'為結(jié)尾量瓜,無法保存任意的字符串。
- 3途乃、保存二進(jìn)制類型绍傲。
- 4、動(dòng)態(tài)管理耍共。C中的字符串操作需要自己管理內(nèi)存的申請(qǐng)和釋放烫饼,redis設(shè)計(jì)了自己的數(shù)據(jù)結(jié)構(gòu),在內(nèi)部封裝了對(duì)內(nèi)存的操作试读,不需要自己維護(hù)杠纵。同時(shí)提供了動(dòng)態(tài)擴(kuò)容,惰性刪除等操作钩骇。
- 5比藻、節(jié)省內(nèi)存。redis區(qū)分了4種不同大小的SDS類型倘屹,分別是SDSHDR8银亲、SDSHDR16、SDSHDR32纽匙、SDSHDR64务蝠、其區(qū)別在于其結(jié)構(gòu)體內(nèi)的len和alloc,表示字符串的最大長(zhǎng)度哄辣。通過attribute((packed))來告訴編譯器通過緊湊的方式來分配內(nèi)存(一般編譯器都是按照8字節(jié)對(duì)齊的方式來分配內(nèi)存)请梢。
SDS源碼分析
SDSHDR5不會(huì)被使用。
sds sdsMakeRoomFor(sds s, size_t addlen) {
void *sh, *newsh;
size_t avail = sdsavail(s);/*剩余可用內(nèi)存*/
size_t len, newlen;
char type, oldtype = s[-1] & SDS_TYPE_MASK;/*8s[-1]z直接獲取到了SDS中的flags*/
int hdrlen;
size_t usable;
/* Return ASAP if there is enough space left. */
if (avail >= addlen) return s;
len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype);
newlen = (len+addlen);
assert(newlen > len); /* Catch size_t overflow */
if (newlen < SDS_MAX_PREALLOC)/*小于1M力穗,則申請(qǐng)需要內(nèi)存的兩倍*/
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;/*否則申請(qǐng)內(nèi)存為所需內(nèi)存?1M*/
type = sdsReqType(newlen);/*計(jì)算最新長(zhǎng)度所需的SDS類型*/
/* Don't use type 5: the user is appending to the string and type 5 is
* not able to remember empty space, so sdsMakeRoomFor() must be called
* at every appending operation. */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;/*不使用 type 5*/
hdrlen = sdsHdrSize(type);
assert(hdrlen + newlen + 1 > len); /* Catch size_t overflow */
if (oldtype==type) {
newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {/*如果是新的類型毅弧,則涉及到字符串的拷貝,此時(shí)會(huì)存在短暫的舊內(nèi)存消耗当窗,所以SDS不應(yīng)該保存較大內(nèi)存的數(shù)據(jù)*/
/* Since the header size changes, need to move the string forward,
* and can't use realloc */
newsh = s_malloc_usable(hdrlen+newlen+1, &usable);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
usable = usable-hdrlen-1;
if (usable > sdsTypeMaxSize(type))
usable = sdsTypeMaxSize(type);
sdssetalloc(s, usable);
return s;
}
sds sdsRemoveFreeSpace(sds s) {
void *sh, *newsh;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
size_t len = sdslen(s);
size_t avail = sdsavail(s);
sh = (char*)s-oldhdrlen;
/* Return ASAP if there is no space left. */
if (avail == 0) return s;
/* Check what would be the minimum SDS header that is just good enough to
* fit this string. */
type = sdsReqType(len);
hdrlen = sdsHdrSize(type);
/* If the type is the same, or at least a large enough type is still
* required, we just realloc(), letting the allocator to do the copy
* only if really needed. Otherwise if the change is huge, we manually
* reallocate the string to use the different header type. */
if (oldtype==type || type > SDS_TYPE_8) {/*如果類型不變或者需要較大的內(nèi)存時(shí)够坐,只是調(diào)用了s_realloc()不做內(nèi)存的真正釋放*/
newsh = s_realloc(sh, oldhdrlen+len+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+oldhdrlen;
} else {/*否則將進(jìn)行數(shù)據(jù)類型的替換,需要釋放原先的類型的內(nèi)存*/
newsh = s_malloc(hdrlen+len+1);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
sdssetalloc(s, len);
return s;
}
redis中SDS的使用
SDS作為redis非常基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)使用非常的廣泛:
- 鍵元咙。
- 客戶端輸入緩沖區(qū)本股。
- slowlog眨攘。
……