Redis底层实现--字符串
Redis字符串存儲(chǔ)實(shí)現(xiàn)原理
- Redis 中的字符串是可以修改的字符串,在內(nèi)存中他是以字節(jié)數(shù)組的形式存在的。我們?cè)谌腴T語(yǔ)言C語(yǔ)言里面的字符串標(biāo)準(zhǔn)形式是以NULL(即0x\0)作為結(jié)束符,但是Redis里面,字符串表示方法不是這樣,因?yàn)?#xff0c;要獲取以null結(jié)尾的字符串需要遍歷整個(gè)字符串,時(shí)間復(fù)雜度是O(n),對(duì)應(yīng)單線程對(duì)外服務(wù)的Redis來(lái)說(shuō)是無(wú)法承受的。
- Redis的字符串結(jié)構(gòu)叫做SDS,Simple Dynamic String。他的結(jié)構(gòu)是一個(gè)帶上長(zhǎng)度信息的字節(jié)數(shù)組,類似C語(yǔ)言中的結(jié)構(gòu)體。
- Redis中SDS存儲(chǔ)結(jié)構(gòu)的設(shè)計(jì)類似于ArrayList機(jī)構(gòu),因?yàn)镽edis允許字符串的修改,因此初始申請(qǐng)可以有一部分的冗余空間。
- capacity 標(biāo)識(shí)所有分配數(shù)組的長(zhǎng)度,包括未存儲(chǔ)數(shù)據(jù)的部分空間
- len標(biāo)識(shí)字符串的實(shí)際長(zhǎng)度
- 當(dāng)冗余的空間不夠時(shí)候,先擴(kuò)容,在復(fù)制舊的內(nèi)容,然后在添加新內(nèi)容,如果字符串長(zhǎng)度非常長(zhǎng),內(nèi)存的分配和復(fù)制開(kāi)銷會(huì)特別大。
- 以上結(jié)構(gòu)體中,使用的泛型T,其中Capacity和len的類型是T,因?yàn)镽edis對(duì)存儲(chǔ)的壓縮優(yōu)化
- 當(dāng)存儲(chǔ)字符串比較短的時(shí)候,了你和capacity可以使用byte和short來(lái)表示,
- Redis規(guī)定字符串長(zhǎng)度不超過(guò)512M,創(chuàng)建字符串時(shí)候len和capacity一樣長(zhǎng),不會(huì)多分配冗余空間,這是因?yàn)榻^大多數(shù)場(chǎng)景下我們不會(huì)去修改字符串。
embstr OR raw
- Redis字符串有兩種存儲(chǔ)方式,在長(zhǎng)度短的時(shí)候,使用embstr形式存儲(chǔ),長(zhǎng)度超過(guò)44 字節(jié)時(shí)候,使用raw形式存儲(chǔ),如下實(shí)驗(yàn):
-
一下我們通過(guò)分析Redis字符串對(duì)象存儲(chǔ)結(jié)構(gòu)來(lái)說(shuō)明兩個(gè)問(wèn)題
- 問(wèn)題一:為什么是44個(gè)字節(jié)作為界限
- 問(wèn)題二:embstr 和raw存儲(chǔ)的區(qū)別
-
Redis對(duì)象存儲(chǔ)都會(huì)有一個(gè)頭部結(jié)構(gòu),如下形式
-
不同的對(duì)象具有不同的type 類型(4bit)。
-
同一個(gè)類型的type也會(huì)有不同的存儲(chǔ)方式encoding(4bit)。
-
為了記錄對(duì)象的lru信息,使用了24bit來(lái)記錄lru信息
-
每個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù),refcount,當(dāng)他歸零時(shí)候,對(duì)象不被任何地方使用,對(duì)象將被銷毀,內(nèi)存被回收
-
ptr指針結(jié)構(gòu)將指向?qū)ο蟮木唧w存儲(chǔ)位置(body)
-
以上所有的綜合一起 4bit+ 4bit+ 24bit + 32 bit + 64bit = 128bit = 16byte,所有Redis對(duì)象的對(duì)象頭結(jié)構(gòu)都需要占據(jù)16字節(jié)存儲(chǔ)空間。
-
接著我們?cè)诜窒鞸DS結(jié)構(gòu)體大小,在字符串比較小的時(shí)候,SDS對(duì)象頭結(jié)構(gòu)的大小如下:
-
如上結(jié)構(gòu)中 capacity ,len, flags 三個(gè)都占用1byte的內(nèi)存,其他的就是 capacity長(zhǎng)度的數(shù)組,用來(lái)存儲(chǔ)具體數(shù)據(jù)。也就是最少也要3 個(gè)字節(jié)的存儲(chǔ)空間。加上上面的16byte,我們一個(gè)沒(méi)有存儲(chǔ)字符串的Redis字符串對(duì)象,已經(jīng)有19 byte的空間被系統(tǒng)各種屬性占用。
-
我們?cè)趦?nèi)存分配的時(shí)候,使用jemalloc, tcmalloc等分配內(nèi)存大小的單元都是2/4/4/8/16/32/64 byte,
-
為了容納完整的embstr對(duì)象,jemalloc最少分配32byte空間,如果字符串稍微長(zhǎng)點(diǎn),那就是64byte,如果字符串超過(guò)64byte,Redis會(huì)認(rèn)為是一個(gè)大字符串,不在適合emdstr存儲(chǔ)的形式,而使用raw形式
-
我們用最大內(nèi)存空間64 來(lái)計(jì)算最大字符串長(zhǎng)度, 64 - 19 = 45 ,但是之前實(shí)驗(yàn)得到的是44
-
SDS結(jié)構(gòu)中content中字符串是以null結(jié)尾,多出這個(gè)字節(jié),便于直接使用glbc的字符串處理函數(shù),以及便于字符串的調(diào)試打印輸出。最終得出了44 的長(zhǎng)度。如下圖:
-
問(wèn)題二中embstr存儲(chǔ)形式與 raw的存儲(chǔ)形式如下
- embst存儲(chǔ)將RedisObject對(duì)象頭結(jié)構(gòu)和SDS對(duì)象連續(xù)存儲(chǔ)在一起,使用malloc方法一次性分配內(nèi)存
- raw存儲(chǔ)形式不一樣,他需要兩次malloc方法,兩個(gè)對(duì)象頭在內(nèi)存地址上不連續(xù)通過(guò)對(duì)象頭中 ptr指針來(lái)尋址存儲(chǔ)位置。
擴(kuò)容策略
- 字符串的擴(kuò)容兩種方式:
- 字符串長(zhǎng)度在1MB之前,擴(kuò)容空間都是加倍擴(kuò)容,也就是保留100%的冗余空間
- 字符串長(zhǎng)度超過(guò)1MB后,避免加倍后冗余空間浪費(fèi)過(guò)多,每次只多分配1MB大小的冗余空間。
上一篇:Redis服務(wù)信息–Info指令
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的Redis底层实现--字符串的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 手机中国移动的视讯中国手机电视业务怎么退
- 下一篇: 数据结构与索引-- mysql Inno