W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
大家好,我是 V 哥。在 Java 中,我們有動態(tài)數(shù)組ArrayList,當插入新元素空間不足時,會進行擴容,好奇 Redis 中的 String 類型,C 語言又是怎樣的實現(xiàn)策略,帶著疑問,咱們來了解一下。
在Redis中,String類型數(shù)據(jù)的擴容主要涉及到SDS(Simple Dynamic String)的內(nèi)存分配機制。SDS是Redis用來存儲字符串的數(shù)據(jù)結(jié)構(gòu),它在C語言的字符數(shù)組基礎(chǔ)上進行了封裝,以支持動態(tài)擴展長度的功能。
當對一個String類型的值進行修改操作(如增加內(nèi)容)時,如果現(xiàn)有的空間不足以容納新的數(shù)據(jù),Redis就會進行擴容。
在Redis中,sdsMakeRoomFor
函數(shù)是用來擴展SDS字符串的緩沖區(qū)的。這個函數(shù)的目的是確保SDS字符串有足夠的空間來追加新的數(shù)據(jù)。以下是sdsMakeRoomFor
函數(shù)的實現(xiàn)邏輯:
sdshdr
結(jié)構(gòu)的free
屬性記錄)是否足夠容納額外的數(shù)據(jù)。如果足夠,函數(shù)直接返回,不需要進行擴容。SDS_MAX_PREALLOC
(通常為1MB),那么Redis會將新長度擴大兩倍,以減少頻繁的內(nèi)存分配操作。如果新長度大于或等于SDS_MAX_PREALLOC
,則會一次性分配足夠的空間,避免每次擴容都只增加少量空間,導(dǎo)致性能下降。s_realloc_usable
(如果類型未變)或s_malloc_usable
(如果類型變化,需要移動數(shù)據(jù))來分配新的內(nèi)存空間。SDS_TYPE_8
變?yōu)?code>SDS_TYPE_16),Redis還需要更新SDS的編碼類型,并可能需要移動數(shù)據(jù)到新的內(nèi)存位置。
在Redis 7.0版本中,SDS的內(nèi)存布局有所變化,不再使用free
屬性,而是使用alloc
屬性來記錄分配的空間總長度,len
屬性記錄已使用的字符串長度。因此,alloc
和len
的差值就代表了空閑空間的大小。這種設(shè)計使得SDS在內(nèi)存布局上更加緊湊,取消了編譯器的對齊,以節(jié)省內(nèi)存空間。
sdsMakeRoomFor
函數(shù)的具體實現(xiàn)如下:
sds _sdsMakeRoomFor(sds s, size_t addlen, int greedy) {
void *sh, *newsh;
size_t avail = sdsavail(s);
size_t len, newlen, reqlen;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
size_t usable;
/* 如果有足夠的剩余空間,直接返回 */
if (avail >= addlen) return s;
len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype);
reqlen = newlen = (len+addlen);
assert(newlen > len); /* Catch size_t overflow */
//判斷是否為greedy模式(為1表示greedy模式)
//是將新長度翻倍還是額外增加`SDS_MAX_PREALLOC`
if (greedy == 1) {
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
}
type = sdsReqType(newlen);
/* 如果類型是SDS_TYPE_5,但是用戶正在追加字符串,那么使用SDS_TYPE_8 */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
hdrlen = sdsHdrSize(type);
assert(hdrlen + newlen + 1 > reqlen); /* 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 {
newsh = s_malloc(hdrlen+newlen+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);
}
usable = usable-hdrlen-1;
if (usable > sdsTypeMaxSize(type))
usable = sdsTypeMaxSize(type);
sdssetalloc(s, usable);
return s;
}
這個函數(shù)首先檢查是否有足夠的空間來追加數(shù)據(jù),如果沒有,則根據(jù)當前的字符串長度和需要追加的數(shù)據(jù)長度來計算新的總長度。如果啟用了greedy
模式,它會根據(jù)是否超過SDS_MAX_PREALLOC
來決定是將新長度翻倍還是額外增加SDS_MAX_PREALLOC
。然后,它會根據(jù)新的總長度來確定新的SDS類型,并分配新的內(nèi)存空間。如果SDS的類型沒有變化,它會使用s_realloc_usable
來擴展現(xiàn)有的內(nèi)存空間;如果類型變化了,它會使用s_malloc
來分配新的內(nèi)存空間,并將舊數(shù)據(jù)復(fù)制到新位置。最后,它會更新SDS頭部信息,包括長度和分配的空間大小。
注意一下哈,在Redis 7.0版本之前的SDS實現(xiàn)和7.0版本之后的實現(xiàn)有哪些變化呢?
在Redis 7.0版本之前,SDS(Simple Dynamic String)的實現(xiàn)主要包括一個頭部結(jié)構(gòu)struct sdshdr
,其中包含了記錄已使用空間的len
字段,記錄未使用空間的free
字段,以及一個字符數(shù)組buf
用于存儲字符串。這種設(shè)計允許SDS在O(1)時間復(fù)雜度內(nèi)獲取字符串長度,并且通過維護free
字段來減少內(nèi)存重分配的次數(shù),提高性能。
然而,在Redis 7.0版本中,SDS的實現(xiàn)發(fā)生了一些變化。首先,引入了一個新的字段flags
,它是一個單字節(jié)的字段,用于存儲SDS的類型信息。這使得SDS的結(jié)構(gòu)更加緊湊,取消了編譯器的對齊,節(jié)省了內(nèi)存空間。其次,free
字段被移除,取而代之的是alloc
字段,它表示SDS的總分配空間。因此,alloc
和len
的差值就代表了空閑空間的大小。這種設(shè)計使得SDS在內(nèi)存布局上更加緊湊,同時保持了動態(tài)擴展長度的功能。
在Redis 7.0版本中,SDS的類型被定義為以下幾種:
SDS_TYPE_5
:長度小于32的字符串,使用flags
的5個最高位存儲長度。SDS_TYPE_8
:長度在1到255之間的字符串,使用1個字節(jié)存儲長度。SDS_TYPE_16
:長度在256到65535之間的字符串,使用2個字節(jié)存儲長度。SDS_TYPE_32
:長度在65536到4294967295之間的字符串,使用4個字節(jié)存儲長度。SDS_TYPE_64
:長度大于4294967295的字符串,使用8個字節(jié)存儲長度。
這種設(shè)計允許SDS根據(jù)字符串的實際長度選擇最合適的頭部類型,從而節(jié)省內(nèi)存。例如,對于短字符串,可以使用SDS_TYPE_5
類型的頭部,它不包含單獨的長度和分配字段,而是將這些信息存儲在flags
字段中。
此外,Redis 7.0版本中的SDS實現(xiàn)還包括了一些其他的優(yōu)化,例如,使用__attribute__ ((__packed__))
來確保結(jié)構(gòu)體在內(nèi)存中緊湊排列,以及通過s_malloc
和s_realloc
等函數(shù)來管理內(nèi)存分配,確保內(nèi)存對齊的同時,也提供了靈活的內(nèi)存管理。
咱們很顯然可以看出,Redis 7.0版本對SDS的實現(xiàn)進行了優(yōu)化,使其更加緊湊和高效,同時也保持了SDS的動態(tài)擴展和二進制安全的特性。這些改進有助于提高Redis在處理大量數(shù)據(jù)時的性能和資源利用率。關(guān)注威哥愛編程,學(xué)習(xí)代碼樂無邊
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: