Redis數(shù)據(jù)類型(二)-字符串對象

通過上一篇文章无蜂,我們可以知道Redis字符串的底層數(shù)據(jù)結(jié)構(gòu)是SDS(Simple Dynamic String)

SDS定義

SDS的結(jié)構(gòu)定義可以從Redis安裝包src/sds.h中看到

sds

Redis定義了五種長度的sds結(jié)構(gòu),基本重要屬性:

len:已使用長度线欲,也就是當(dāng)前字符串已使用內(nèi)存大小

alloc:總分配的內(nèi)存大小造烁,可以理解為buf[]的長度

flag:第三位表示當(dāng)前sds的類型否过,高五位沒用

buf[]:字節(jié)數(shù)組,用于保存字符串惭蟋,字節(jié)\0結(jié)尾苗桂,占用1個(gè)字節(jié)

sds的類型:

#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4

從注釋可以看出,sdshdr5這個(gè)數(shù)據(jù)結(jié)構(gòu)沒有使用告组。sdshdr5只是對于key/value中煤伟,value為字符串時(shí)沒有使用,當(dāng)key很小時(shí)木缝,還是有用便锨。sdshdr5字符串的長度適用于長度小于32位的字符串(1<<5,即2^5=32)我碟,且少了兩個(gè)關(guān)鍵字段放案,不利于動態(tài)擴(kuò)容,當(dāng)于預(yù)分配的內(nèi)存用完時(shí)矫俺,就需要重新分配內(nèi)存且進(jìn)行數(shù)據(jù)復(fù)制遷移吱殉,對性能影響比較大〉г現(xiàn)在對于value,最小的字符串都是用sdshdr8開始存儲考婴。

sds-lengh
SDS與C語言字符串的區(qū)別

sds相比于C語言原聲字符串的優(yōu)勢:

  • 記錄了長度信息贩虾,獲取字符串長度信息的時(shí)間復(fù)雜度為O(1)催烘,c語言獲取字符串長度每次都需要便利字節(jié)數(shù)組沥阱,時(shí)間復(fù)雜度為O(N)。
  • c語言對字符串拼接伊群,每次都需要重新分配內(nèi)存并進(jìn)行數(shù)據(jù)遷移考杉。sds有預(yù)分配內(nèi)存,當(dāng)內(nèi)存足夠時(shí)舰始,不需要進(jìn)行內(nèi)存的重新分配和數(shù)據(jù)復(fù)制遷移崇棠。
  • sds還是保持了\0結(jié)尾,這樣可以直接調(diào)用c語言的原聲方法丸卷。

sds空間預(yù)分配:

  • 對sds修改后枕稀,sds的長度小于1MB,那么Redis會分配一個(gè)同樣大小未使用空間谜嫉,即sds的len和未使用空間是一樣的萎坷。比如修改,sds的len變?yōu)榱?0個(gè)字節(jié)沐兰,那么sds的總長度會變成20個(gè)字節(jié)哆档。

  • 對sds修改后,sds的長度大于1MB住闯,每次擴(kuò)容都會以1MB的長度擴(kuò)容瓜浸。比如修改后sds的長度為2MB,那么分配后的總長度為3MB比原,未使用長度是1MB插佛。

sds惰性釋放:

對字符串進(jìn)行縮減操作,Redis不會立馬釋放內(nèi)存量窘,而是繼續(xù)空在那里朗涩,等待以后使用。

字符串編碼

由上一節(jié)我們知道绑改,每個(gè)對象谢床,除了有數(shù)據(jù)結(jié)構(gòu)對象外,還有一個(gè)編碼類型厘线,字符串對象有三種編碼類型:int识腿、raw、embstr造壮。
Redis數(shù)據(jù)結(jié)構(gòu)

如果一個(gè)字符串對象保存的是整數(shù)值渡讼,并且這個(gè)整數(shù)值可以用一個(gè)long類型來表示骂束,那么字符串對象就會將整數(shù)值保存在字符串結(jié)構(gòu)中的*ptr屬性里面,并且將編碼類型設(shè)置為int成箫。

? [圖片上傳失敗...(image-49f796-1604411712176)]

如果一個(gè)字符串對象保存的是一個(gè)字符串展箱,并且改字符串的長度大于44字節(jié),那么字符串對象就會用SDS來保存字符串蹬昌,并且將編碼設(shè)置為raw類型

redis_string_encoing_raw

如果一個(gè)字符串對象保存的是一個(gè)字符串混驰,并且改字符串的長度小于等于44字節(jié),那么字符串對象就會用SDS來保存字符串皂贩,并且將編碼設(shè)置為embstr類型

redis_string_encoing_embstr

embstr是一種專門用來保存短字符串的編碼方式栖榨,它只會調(diào)用一次內(nèi)存分配函數(shù)來分配一塊連續(xù)的內(nèi)存用來保存redisObject結(jié)構(gòu)和sdshdr結(jié)構(gòu)。而raw會調(diào)用兩次內(nèi)存分配函數(shù)來分別分配redisObject結(jié)構(gòu)內(nèi)存和shshdr內(nèi)存明刷。

為什么是44個(gè)字節(jié)

我們看下一個(gè)redisObject最小有多少個(gè)字節(jié)

typedf struct RedisObject {
int4 type; // 4bits
int4 encoding; // 4bits
int24 lru; // 24bits 3byte
int32 refcount; // 4byte
void *ptr; // 8byte婴栽,64-bit system
} robj;

4bit+4bit+24bit+32bit+64bit=128bit=16byte

*ptr指向的是一個(gè)sds結(jié)構(gòu),sds的基本結(jié)構(gòu)如下:

struct SDS{
  int8 len;     //1byte
  int8 alloc; //1byte
  int8 flags; //1byte
  char buf[]; //內(nèi)聯(lián)數(shù)組辈末,長度為capacity
}

一個(gè)sds結(jié)構(gòu)對象頭的大小是capacity+3

這就意味著一個(gè)字符串對象的最下空間是16+3=19個(gè)字節(jié)(capacity為0)

內(nèi)存分配函數(shù)分配內(nèi)存的大小單位一般是2愚争、4、8挤聘、16轰枝、32、64等字節(jié)長度檬洞。所以為了容納一個(gè)完成的字符串對象狸膏,malloc函數(shù)最少會分配一個(gè)32字節(jié)的內(nèi)存,當(dāng)字符串稍微長一點(diǎn)的時(shí)候添怔,就會分配64字節(jié)的空間湾戳。當(dāng)字符串對象總體超過64字節(jié)時(shí),Redis會認(rèn)為這個(gè)字符串對象是一個(gè)大字符串广料,會用raw的形式存儲砾脑。

一個(gè)字符串對象總體如果為64字節(jié),那么能夠存儲的字符串的長度就是64-19=45字節(jié)艾杏,字符串是以字節(jié)\0結(jié)尾韧衣,占用一個(gè)字節(jié),64字節(jié)的字符串對象购桑,能容納的字符串就是44字節(jié)畅铭。這就是為什么當(dāng)字符串長度超過44字節(jié)時(shí),Redis才用raw的形式存儲字符串勃蜘。

文章持續(xù)更新硕噩,歡迎大家關(guān)注我公眾號,微信搜索「易大師的小屋」缭贡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炉擅,一起剝皮案震驚了整個(gè)濱河市辉懒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谍失,老刑警劉巖眶俩,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異快鱼,居然都是意外死亡颠印,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門攒巍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗽仪,“玉大人荒勇,你說我怎么就攤上這事柒莉。” “怎么了沽翔?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵兢孝,是天一觀的道長。 經(jīng)常有香客問我仅偎,道長跨蟹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任橘沥,我火速辦了婚禮窗轩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘座咆。我一直安慰自己痢艺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布介陶。 她就那樣靜靜地躺著堤舒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哺呜。 梳的紋絲不亂的頭發(fā)上舌缤,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音某残,去河邊找鬼国撵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛玻墅,可吹牛的內(nèi)容都是我干的介牙。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼椭豫,長吁一口氣:“原來是場噩夢啊……” “哼耻瑟!你這毒婦竟也來了旨指?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤喳整,失蹤者是張志新(化名)和其女友劉穎谆构,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體框都,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搬素,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了魏保。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熬尺。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖谓罗,靈堂內(nèi)的尸體忽然破棺而出粱哼,到底是詐尸還是另有隱情,我是刑警寧澤檩咱,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布揭措,位于F島的核電站,受9級特大地震影響刻蚯,放射性物質(zhì)發(fā)生泄漏绊含。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一炊汹、第九天 我趴在偏房一處隱蔽的房頂上張望躬充。 院中可真熱鬧,春花似錦讨便、人聲如沸充甚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽津坑。三九已至,卻和暖如春傲霸,著一層夾襖步出監(jiān)牢的瞬間疆瑰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工昙啄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穆役,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓梳凛,卻偏偏與公主長得像耿币,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子韧拒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345