Redis數(shù)據(jù)結(jié)構(gòu)與對象——壓縮列表

壓縮列表(ziplist)是列表和哈希鍵的底層實現(xiàn)之一储耐。

1 壓縮列表的構(gòu)成

??壓縮列表是Redis節(jié)約成本而開發(fā),是由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型數(shù)據(jù)結(jié)構(gòu)滨溉。一個壓縮列表可以包含任意多個節(jié)點(diǎn)什湘,每個節(jié)點(diǎn)可以保存一個字節(jié)數(shù)組或一個整數(shù)值。
??下圖表示壓縮的各個組成部分及含義


??下圖表示一個包含三個節(jié)點(diǎn)的壓縮列表晦攒,列表的起始地址的指針為p闽撤,那么列表表尾節(jié)點(diǎn)的地址就是p加上zltail的值,即p+60脯颜。
包含是三個節(jié)點(diǎn)的壓縮列表

2 壓縮列表節(jié)點(diǎn)的構(gòu)成

??每個壓縮列表節(jié)點(diǎn)可以保存一個字節(jié)數(shù)組或者一個整數(shù)值哟旗。下圖表示壓縮列表的組成部分及含義。


壓縮列表節(jié)點(diǎn)各個組成部分和含義

??2.1 previous_entry_length

??節(jié)點(diǎn)的previous_entry_length屬性以字節(jié)為單位栋操,長度為1字節(jié)或5字節(jié)闸餐,記錄壓縮列表前一個節(jié)點(diǎn)的長度。

(1) 如果前一個節(jié)點(diǎn)的長度小于254字節(jié)矾芙,那么previous_entry_length屬性長度為1字節(jié)舍沙。
(2) 如果前一個節(jié)點(diǎn)的長度大于等于254字節(jié),那么previous_entry_length屬性長度為5字節(jié)剔宪。

??因為節(jié)點(diǎn)的previous_entry_length記錄了前一個節(jié)點(diǎn)的長度拂铡,所以程序可以通過指針運(yùn)算壹无,根據(jù)當(dāng)前節(jié)點(diǎn)的起始地址來計算出前一個節(jié)點(diǎn)的起始地址。壓縮列表的從表尾向表頭遍歷操作就是通過這一原理實現(xiàn)的和媳。只要擁有了一個指向某個節(jié)點(diǎn)起始地址的指針格遭,就可以通過這個指針和這個節(jié)點(diǎn)的previous_entry_length屬性,一個一個向前遍歷留瞳,最終達(dá)到列表的表頭節(jié)點(diǎn)拒迅。

??2.2 encoding

??節(jié)點(diǎn)的encoding屬性記錄了節(jié)點(diǎn)的content屬性所保存數(shù)據(jù)的類型和長度:

(1) 如果是一個字節(jié)長,值的最高位是11開頭的是整數(shù)編碼她倘,表示content保存的是整數(shù)值璧微。
(2) 如果是一個字節(jié)、兩個字節(jié)或者五個字節(jié)長硬梁,值的最高位為00前硫、01或10的是字節(jié)數(shù)組編碼,表示content保存的是字節(jié)數(shù)組荧止。

??2.3 content

??節(jié)點(diǎn)的content屬性負(fù)責(zé)保存的值屹电,節(jié)點(diǎn)值可以是一個字節(jié)數(shù)組或者整數(shù),值的類型和長度由節(jié)點(diǎn)的encoding屬性決定跃巡。
??如下圖危号,表示一個保存字節(jié)數(shù)組的列表節(jié)點(diǎn),encoding屬性的最高兩個二進(jìn)制位00表示節(jié)點(diǎn)保存的是一個字節(jié)數(shù)組素邪。后六個二進(jìn)制位001011表示字節(jié)數(shù)組的長度外莲,即“hello world”的長度。


一個保存字節(jié)數(shù)組的列表節(jié)點(diǎn)

??如下圖兔朦,表示一個保存整數(shù)值的列表節(jié)點(diǎn)偷线,encoding屬性的最高兩個二進(jìn)制位11表示節(jié)點(diǎn)保存的是一個整數(shù)值,content屬性保存節(jié)點(diǎn)的值10086沽甥。


一個保存整數(shù)值的列表節(jié)點(diǎn)

3 連鎖更新

??前面說過声邦,previous_entry_length屬性的長度是1個字節(jié)或5個字節(jié),如果前一個節(jié)點(diǎn)的長度小于254字節(jié)安接,就使用1個字節(jié)保存這個長度值翔忽,反之使用5個字節(jié)保存這個長度值。
??假設(shè)在壓縮列表中盏檐,有多個連續(xù)的歇式、長度介于250字節(jié)到253字節(jié)之間的節(jié)點(diǎn)e1至eN,如下圖所示:


??由于每個節(jié)點(diǎn)的長度均小于254胡野,所以每個節(jié)點(diǎn)的previous_entry_length屬性所需的長度都是1個字節(jié)材失。
??此時,在表頭插入一個新節(jié)點(diǎn)硫豆,這個節(jié)點(diǎn)的長度大于254龙巨,由于 e1的previous_entry_length屬性僅長1字節(jié)笼呆,沒有辦法保存新節(jié)點(diǎn)的長度,所以長須需要將e1節(jié)點(diǎn)的previous_entry_length屬性從原來的1個字節(jié)擴(kuò)展為5個字節(jié)長旨别。

??由于e1的previous_entry_length屬性添加了4個字節(jié)诗赌,導(dǎo)致e1節(jié)點(diǎn)的長度也大于254,所以導(dǎo)致e2節(jié)點(diǎn)previous_entry_length屬性也要從原來的1個字節(jié)擴(kuò)展為5個字節(jié)長...直到eN為止秸弛。
??Redis在特殊情況下產(chǎn)生連續(xù)多次空間擴(kuò)展操作稱之為“連鎖更新”(cascade update)铭若,下圖表示了這一過程。
連鎖更新過程

??除了新添加節(jié)點(diǎn)會造成連鎖跟新之外递览,刪除節(jié)點(diǎn)也可能引發(fā)連鎖更新叼屠。
??如下圖所示的壓縮列表,e1至eN都是大小介于250字節(jié)到253字節(jié)的節(jié)點(diǎn)绞铃,big節(jié)點(diǎn)時長度大于254的節(jié)點(diǎn)镜雨,small節(jié)點(diǎn)也是小于254字節(jié)的節(jié)點(diǎn)。small節(jié)點(diǎn)使用5個字節(jié)保存big節(jié)點(diǎn)的長度儿捧,如果此時將small節(jié)點(diǎn)刪除荚坞,那么需要e1節(jié)點(diǎn)保存big節(jié)點(diǎn)的長度,但是e1節(jié)點(diǎn)的previous_entry_length屬性是1個字節(jié)菲盾,需要擴(kuò)展...西剥,導(dǎo)致的情況和插入的情況一致。
刪除節(jié)點(diǎn)引發(fā)的連鎖更新

??因為連鎖更新在最壞的情況下需要對壓縮列表執(zhí)行N次空間重分配操作亿汞,而每次空間分配的最壞復(fù)雜度為O(N),所以連鎖更新的最壞復(fù)雜度為O(N2)揪阿。
??但是疗我,要造成連鎖更新的可能性非常小。首先需要多個連續(xù)的南捂、長度均介于250字節(jié)至253字節(jié)的節(jié)點(diǎn)吴裤,這種情況實際上并不多見。同時溺健,即使出現(xiàn)了連鎖更新麦牺,但是只要被更新的節(jié)點(diǎn)數(shù)量不多,就不會對性能造成任何影響鞭缭。

4 小結(jié)

(1) 壓縮列表是一種為節(jié)約內(nèi)存而開發(fā)的順序型數(shù)據(jù)結(jié)構(gòu)剖膳,被作為列表鍵和哈希鍵的底層實現(xiàn)。
(2) 壓縮節(jié)點(diǎn)可以包含任意個節(jié)點(diǎn)岭辣,每個節(jié)點(diǎn)可以保存一個字節(jié)數(shù)據(jù)或整數(shù)值吱晒。
(3) 添加新節(jié)點(diǎn)到壓縮列表,或者從壓縮列表中刪除節(jié)點(diǎn)沦童,可能引發(fā)連鎖更新操作仑濒,但是這操作出現(xiàn)的幾率并不高叹话。

??本文完


??注:本文參考《Redis設(shè)計與實現(xiàn)》,如發(fā)現(xiàn)錯誤墩瞳,請指正驼壶!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市喉酌,隨后出現(xiàn)的幾起案子热凹,更是在濱河造成了極大的恐慌,老刑警劉巖瞭吃,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碌嘀,死亡現(xiàn)場離奇詭異,居然都是意外死亡歪架,警方通過查閱死者的電腦和手機(jī)股冗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來和蚪,“玉大人止状,你說我怎么就攤上這事≡芘” “怎么了怯疤?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長催束。 經(jīng)常有香客問我集峦,道長,這世上最難降的妖魔是什么抠刺? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任塔淤,我火速辦了婚禮,結(jié)果婚禮上速妖,老公的妹妹穿的比我還像新娘高蜂。我一直安慰自己,他們只是感情好罕容,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布备恤。 她就那樣靜靜地躺著,像睡著了一般锦秒。 火紅的嫁衣襯著肌膚如雪露泊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天脂崔,我揣著相機(jī)與錄音滤淳,去河邊找鬼。 笑死砌左,一個胖子當(dāng)著我的面吹牛脖咐,可吹牛的內(nèi)容都是我干的铺敌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼屁擅,長吁一口氣:“原來是場噩夢啊……” “哼偿凭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起派歌,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弯囊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后胶果,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匾嘱,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年早抠,在試婚紗的時候發(fā)現(xiàn)自己被綠了霎烙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡蕊连,死狀恐怖悬垃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情甘苍,我是刑警寧澤尝蠕,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站载庭,受9級特大地震影響看彼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜囚聚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一闲昭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧靡挥,春花似錦、人聲如沸鸯绿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓶蝴。三九已至毒返,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舷手,已是汗流浹背拧簸。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留男窟,地道東北人盆赤。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓贾富,卻偏偏與公主長得像,于是被迫代替她去往敵國和親牺六。 傳聞我的和親對象是個殘疾皇子颤枪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容