redis——数据结构(整数集合,压缩列表)
4、整數(shù)集合
整數(shù)集合(intset)是 Redis 用于保存整數(shù)值的集合抽象數(shù)據(jù)結(jié)構(gòu), 可以保存?int16_t?、?int32_t?、?int64_t?的整數(shù)值, 并且保證集合中不會(huì)出現(xiàn)重復(fù)元素。
實(shí)現(xiàn)較為簡(jiǎn)單:
typedef struct intset {// 編碼方式uint32_t encoding;// 集合包含的元素?cái)?shù)量uint32_t length;// 保存元素的數(shù)組int8_t contents[]; } intset;各個(gè)項(xiàng)在數(shù)組中從小到大有序地排列, 并且數(shù)組中不包含任何重復(fù)項(xiàng)。
雖然?intset?結(jié)構(gòu)將?contents?屬性聲明為?int8_t?類型的數(shù)組, 但實(shí)際上?contents?數(shù)組并不保存任何?int8_t?類型的值 ——?contents?數(shù)組的真正類型取決于?encoding?屬性的值:
如果?encoding?屬性的值為?INTSET_ENC_INT16?, 那么?contents?就是一個(gè)?int16_t?類型的數(shù)組, 數(shù)組里的每個(gè)項(xiàng)都是一個(gè)?int16_t?類型的整數(shù)值 (最小值為?-32,768?,最大值為?32,767?)。
如果?encoding?屬性的值為?INTSET_ENC_INT32?, 那么?contents?就是一個(gè)?int32_t?類型的數(shù)組, 數(shù)組里的每個(gè)項(xiàng)都是一個(gè)?int32_t?類型的整數(shù)值 (最小值為?-2,147,483,648?,最大值為?2,147,483,647?)。
如果?encoding?屬性的值為?INTSET_ENC_INT64?, 那么?contents?就是一個(gè)?int64_t?類型的數(shù)組, 數(shù)組里的每個(gè)項(xiàng)都是一個(gè)?int64_t?類型的整數(shù)值 (最小值為?-9,223,372,036,854,775,808?,最大值為?9,223,372,036,854,775,807?)。
? ? 升級(jí)
c語(yǔ)言是靜態(tài)類型語(yǔ)言,不允許不同類型保存在一個(gè)數(shù)組。這樣第一,靈活性較差,第二,有時(shí)會(huì)用掉不必要的內(nèi)存
比如用long long儲(chǔ)存1
為了提高整數(shù)集合的靈活性和節(jié)約內(nèi)存,我們引入升級(jí)策略。
當(dāng)我們要將一個(gè)新元素添加到集合里, 并且新元素類型比集合現(xiàn)有元素的類型都要長(zhǎng)時(shí), 集合需要先進(jìn)行升級(jí)。
分為三步進(jìn)行:
因?yàn)槊看翁砑有略囟伎赡軙?huì)引起升級(jí), 每次升級(jí)都要對(duì)已有元素類型轉(zhuǎn)換, 所以添加新元素的時(shí)間復(fù)雜度為?O(N)?。
因?yàn)橐l(fā)升級(jí)的新元素比原數(shù)據(jù)都長(zhǎng),所以要么他是最大的,要么他是最小的。我們把它放在開(kāi)頭或結(jié)尾即可。
?
? ? 降級(jí)
略略略,不管你們信不信,整數(shù)集合不支持降級(jí)操作。。我也不知道為啥
5、壓縮列表
?
壓縮列表是列表鍵和哈希鍵的底層實(shí)現(xiàn)之一。
當(dāng)一個(gè)列表鍵只包含少量列表項(xiàng),并且列表項(xiàng)都是小整數(shù)或者短字符串,redis就會(huì)用壓縮列表做列表鍵底層實(shí)現(xiàn)。
壓縮列表是 Redis 為了節(jié)約內(nèi)存而開(kāi)發(fā)的, 由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型(sequential)數(shù)據(jù)結(jié)構(gòu)。
一個(gè)壓縮列表可以包含任意多個(gè)節(jié)點(diǎn)(entry), 每個(gè)節(jié)點(diǎn)可以保存一個(gè)字節(jié)數(shù)組或者一個(gè)整數(shù)值。
具體實(shí)現(xiàn):
具體說(shuō)一下entry:
由三個(gè)部分組成:
1、previous_entry_length:記錄上一個(gè)節(jié)點(diǎn)的長(zhǎng)度,這樣我們就可以從最后一路遍歷到開(kāi)頭。
2、encoding:記錄了content所保存的數(shù)據(jù)類型和長(zhǎng)度。(具體編碼不寫了,不重要)
3、content:保存節(jié)點(diǎn)值,可以是字節(jié)數(shù)組或整數(shù)。(具體怎么壓縮的等我搞明白再補(bǔ))
? ? 連鎖更新
前面說(shuō)過(guò), 每個(gè)節(jié)點(diǎn)的?previous_entry_length?屬性都記錄了前一個(gè)節(jié)點(diǎn)的長(zhǎng)度:
- 如果前一節(jié)點(diǎn)的長(zhǎng)度<?254?KB, 那么?previous_entry_length?需要用?1?字節(jié)長(zhǎng)的空間
- 如果前一節(jié)點(diǎn)的長(zhǎng)度>=254?KB, 那么?previous_entry_length?需要用?5?字節(jié)長(zhǎng)的空間
現(xiàn)在, 考慮這樣一種情況: 在一個(gè)壓縮列表中, 有多個(gè)連續(xù)的、長(zhǎng)度介于?250?字節(jié)到?253?字節(jié)之間的節(jié)點(diǎn) ,這時(shí), 如果我們將一個(gè)長(zhǎng)度大于等于?254?字節(jié)的新節(jié)點(diǎn)?new?設(shè)置為壓縮列表的表頭節(jié)點(diǎn)。。。。
然后腦補(bǔ)一下,就會(huì)導(dǎo)致連鎖擴(kuò)大每個(gè)節(jié)點(diǎn)的空間對(duì)吧?e(i)因?yàn)閑(i-1)的擴(kuò)大而擴(kuò)大,i+1也是如此,以此類推。。。
?
刪除節(jié)點(diǎn)同樣會(huì)導(dǎo)致連鎖更新。
這個(gè)事情只是想說(shuō)明一個(gè)問(wèn)題:插入刪除操作的最壞時(shí)間復(fù)雜度其實(shí)是o(n*n),因?yàn)槊扛乱粋€(gè)節(jié)點(diǎn)都要o(n)。
但是,也不用太過(guò)擔(dān)心,因?yàn)檫@種特殊情況并不多見(jiàn),這些命令的平均復(fù)雜度依舊是o(n)。
總結(jié)
以上是生活随笔為你收集整理的redis——数据结构(整数集合,压缩列表)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 贪吃蛇源代码111
- 下一篇: 数据结构课上笔记10