Redis數(shù)據(jù)結(jié)構(gòu)類型示例解析
intset
當(dāng)set集合存儲(chǔ)的是整數(shù)時(shí),encoding為intset類型(小整數(shù)集合)
typedef struct intset {
int32 encoding;
int32 length;
int contents[];
}
| 字段 | 描述 | 說明 |
|---|---|---|
| encoding | 決定整數(shù)位寬是16位、32位還是64位 | 枚舉表示 |
| length | 元素個(gè)數(shù) | |
| contents | 整數(shù)數(shù)組,存儲(chǔ)元素值 |
intset按照從小到大的順序保存元素。存儲(chǔ)元素時(shí),根據(jù)整數(shù)大小決定是否要將encoding升級(jí),找到要插入元素的位置,如果不是最后一位,會(huì)將所在位置之后的元素后移一位,最后插入元素。如果插入的元素不為整數(shù),存儲(chǔ)形式將變成hash結(jié)構(gòu)。
ziplist
當(dāng)hash與zset滿足如下條件條件時(shí),編碼類型為ziplist(壓縮列表),具體可在配置文件中查看。
hash-max-ziplist-entries 512 # 當(dāng)hash元素個(gè)數(shù)小于512時(shí) hash-max-ziplist-value 64 # 當(dāng)hash鍵或值長(zhǎng)度小于64時(shí) zset-max-ziplist-entries 128 # 當(dāng)zset元素個(gè)數(shù)小于128時(shí) zset-max-ziplist-value 64 # 當(dāng)zset值小于64時(shí)
typedef struct ziplist {
int32 zlbytes;
int32 zltail_offset;
int16 zllength;
T[] entries;
int8 zlend;
}
typedef struct entry {
int<var> prevlen;
int<var> encoding;
byte[] content;
}
| 字段 | 描述 | 說明 |
|---|---|---|
| zlbytes | ziplist所占字節(jié)數(shù) | |
| zltail_offset | 最后一個(gè)元素距離壓縮列表起始位置的偏移量 | 用于快速定位到最后一個(gè)節(jié)點(diǎn),然后倒序遍歷 |
| zllength | 元素個(gè)數(shù) | |
| entries | 壓縮元素 | |
| zlend | 標(biāo)志壓縮列表的結(jié)束 | 恒為FF |
| 字段 | 描述 | 說明 |
|---|---|---|
| prevlen | 前一個(gè)entry的字節(jié)長(zhǎng)度 | 第一個(gè)entry恒為0,字節(jié)長(zhǎng)度動(dòng)態(tài)變化,當(dāng)字符串長(zhǎng)度小于254時(shí),用一個(gè)字節(jié),否則用五個(gè)字節(jié) |
| encoding | 編碼類型 | 編碼類型根據(jù)元素內(nèi)容動(dòng)態(tài)變化,極為復(fù)雜,本篇不作詳細(xì)描述,具體可搜索ziplist編碼類型 |
| content | 元素內(nèi)容,可選 |
下圖是一個(gè)ziplist的demo

- 第1-4字節(jié),zlbytes為25,說明該壓縮列表共占用25個(gè)字節(jié)
- 第5-8字節(jié),zltail_offset為22,說明最后一個(gè)元素從22開始
- 第9-10字節(jié),zllength為3,說明共有3個(gè)元素
- 第11-16字節(jié),第一個(gè)entry: 其中prevlen=0,因?yàn)樗懊鏇]有數(shù)據(jù)項(xiàng);encoding=4,表示后面4byte按照字符串存儲(chǔ),數(shù)據(jù)的值為name
- 第17-21字節(jié),第二個(gè)entry: 其中prevlen=6,表示前一個(gè)entry共占用6byte;encoding=3,表示后面3byte按照字符串存儲(chǔ),數(shù)據(jù)的值為why
- 第22-24字節(jié),第三個(gè)entry: 其中prevlen=5,表示前一個(gè)entry共占用5byte;encoding=0xFE,表示后面1byte存儲(chǔ)整數(shù),數(shù)據(jù)的值為14
- 第25字節(jié),zlend為FF,標(biāo)志壓縮列表的結(jié)束
當(dāng)用ziplist存儲(chǔ)hash結(jié)構(gòu)時(shí),將key與value分別當(dāng)作一個(gè)entry存儲(chǔ)。
可見壓縮列表存儲(chǔ)非常的緊湊,當(dāng)某一個(gè)entry長(zhǎng)度變?yōu)?54時(shí),下一個(gè)entry的prevlen將從1個(gè)字節(jié)擴(kuò)展到5個(gè)字節(jié),這就是級(jí)聯(lián)更新
quicklist
quicklist(快速列表)用于存儲(chǔ)list集合,它是ziplist與linkedlist的混合體,linkedlist與雙向列表結(jié)構(gòu)類似。
quicklist內(nèi)部默認(rèn)單個(gè)ziplist長(zhǎng)度為8K,超過這個(gè)長(zhǎng)度,就會(huì)另起一個(gè)node,可在配置文件中配置。
# -2表示8k,枚舉類型可在配置文件中查看 list-max-ziplist-size -2
quicklist默認(rèn)的壓縮深度為0,也就是不壓縮。如果壓縮深度為1,那么就是首尾不壓縮,如果壓縮深度為2,那么就是首2個(gè)、尾2個(gè)不壓縮,可在配置文件中配置。
list-compress-depth 0
skiplist
zset使用dict存儲(chǔ)value與score的映射,另一方面還需要按照score提供排序功能,于是就有了skiplist(跳躍列表)
先看skiplist的一個(gè)demo

typedef struct zsl {
zslnode* header;
zslnode* tail;
int maxLevel;
}
typedef struct zslnode {
sds value;
double score;
zslforward*[] forwards;
zslnode* backward;
}
typedef struct zslforward {
zslnode* item;
int span;
}
| 字段 | 描述 | 說明 |
|---|---|---|
| header | 指向跳躍列表的頭指針 | value固定為NULL,score固定為0,backward為null |
| tail | 指向跳躍列表的尾指針 | |
| maxLevel | 當(dāng)前跳躍表最大層數(shù) | 最大為64 |
| value | 用于存儲(chǔ)字符串類型的數(shù)據(jù) | |
| score | 用于存儲(chǔ)分值 | |
| backward | 回退節(jié)點(diǎn) | 圖中的←箭頭 |
| forwards | 前進(jìn)節(jié)點(diǎn) | 圖中的→箭頭,每一層對(duì)應(yīng)一個(gè) |
| span | 跨度,存儲(chǔ)一個(gè)節(jié)點(diǎn)跳到下一個(gè)節(jié)點(diǎn)中間跳過了多少節(jié)點(diǎn) | 如score1指向score5,則span值為4,這是排名的實(shí)現(xiàn)原理 |
最小分值的backward固定null,對(duì)于每一個(gè)新插入的節(jié)點(diǎn),會(huì)調(diào)用一個(gè)隨機(jī)算法,來給它分配一個(gè)合理的層數(shù)
level1的概率為1-0.25=0.75,實(shí)際為100%,因?yàn)樘S列表的最小層數(shù)為1
level2的概率為0.75*0.25=0.1875level3的概率為0.1875*0.25=0.0468 ......
leveln的概率為(1-0.25)*Math.pow(0.25,n-1)
總結(jié)
Redis作為單線程內(nèi)存服務(wù),在響應(yīng)、數(shù)據(jù)結(jié)構(gòu)上作出了很多的優(yōu)化,值得我們學(xué)習(xí)
| 對(duì)象類型 | 編碼類型 |
|---|---|
| string | int、raw、embstr |
| list | quicklist |
| hash | dict、ziplist |
| set | intset、dict |
| zset | ziplist、skiplist+dict |
HyperLogLog
HyperLogLog的原理為伯努利試驗(yàn),即丟硬幣,根據(jù)連續(xù)出現(xiàn)反面的次數(shù)X,推算出一共丟了2的X次方次硬幣,當(dāng)X很大時(shí),推算出來的總數(shù)與實(shí)際總數(shù)誤差就很接近了。具體可查詢其他文章。
pfadd
element經(jīng)過hash算法之后是一個(gè)64位的固定值
低14位為桶
查找高50位第一個(gè)為1的位數(shù),如果大于當(dāng)前桶的位數(shù),就將其設(shè)置為當(dāng)前桶的位數(shù)
假設(shè)hash值是 :{此處省略45位}01100 00000000000101
- 低14位的二進(jìn)制轉(zhuǎn)為10進(jìn)制,值為5(regnum),即我們把數(shù)據(jù)放在第5個(gè)桶
- 高50位第一個(gè)1的位置是3,即count值為3
- registers[5]取出歷史值oldcount
- 如果count > oldcount,則更新 registers[5] = count
- 如果count <= oldcount,則不做任何處理
HyperLogLog用了16384個(gè)桶,每個(gè)桶占用6bit,因此說一個(gè)HyperLogLog所占用內(nèi)存是12K。
調(diào)和平均數(shù):
假設(shè)我的工資為10_000,馬云的工資為1_000_000,那我和馬云的平均工資為505_000,我肯定是不認(rèn)同的。。。
如果使用調(diào)和平均數(shù),則為2/(1/10_000+1/1_000_000)=19_801
同理,桶位數(shù)的平均數(shù)為:n/(1/桶1位數(shù)+1/桶2位數(shù)+...+1/桶n位數(shù))
桶的平均個(gè)數(shù)為:Math.pow(2,桶位數(shù)的平均數(shù))
總數(shù)量:const*桶總數(shù)n*桶的平均個(gè)數(shù),其中constant為不定值,與桶個(gè)數(shù)有關(guān),假設(shè)m為桶個(gè)數(shù),取對(duì)數(shù)
pfcount
p=log2m
switch (p) {
case 4:
constant = 0.673 * m * m;
case 5:
constant = 0.697 * m * m;
case 6:
constant = 0.709 * m * m;
default:
constant = (0.7213 / (1 + 1.079 / m)) * m * m;
}以上就是Redis數(shù)據(jù)結(jié)構(gòu)類型示例解析的詳細(xì)內(nèi)容,更多關(guān)于Redis數(shù)據(jù)結(jié)構(gòu)類型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- redis底層數(shù)據(jù)結(jié)構(gòu)之ziplist實(shí)現(xiàn)詳解
- Redis數(shù)據(jù)結(jié)構(gòu)之intset整數(shù)集合使用學(xué)習(xí)
- Redis數(shù)據(jù)結(jié)構(gòu)之跳躍表使用學(xué)習(xí)
- Redis數(shù)據(jù)結(jié)構(gòu)之listpack和quicklist使用學(xué)習(xí)
- Redis數(shù)據(jù)結(jié)構(gòu)面試高頻問題解析
- Redis數(shù)據(jù)結(jié)構(gòu)原理淺析
- redis底層數(shù)據(jù)結(jié)構(gòu)之skiplist實(shí)現(xiàn)示例
相關(guān)文章
jwt+redis實(shí)現(xiàn)登錄認(rèn)證的示例代碼
在登錄業(yè)務(wù)代碼中,當(dāng)用戶登錄成功時(shí),生成一個(gè)登錄憑證存儲(chǔ)到redis中,本文主要介紹了jwt+redis實(shí)現(xiàn)登錄認(rèn)證的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
Redis結(jié)合 Docker 搭建集群并整合SpringBoot的詳細(xì)過程
這篇文章主要介紹了Redis結(jié)合Docker搭建集群并整合SpringBoot的詳細(xì)過程,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06
Redis實(shí)現(xiàn)用戶關(guān)注的項(xiàng)目實(shí)踐
本文主要介紹了Redis實(shí)現(xiàn)用戶關(guān)注的項(xiàng)目實(shí)踐,通過使用Redis的set數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)關(guān)注對(duì)象,方便高效地進(jìn)行添加和取消關(guān)注操作,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
Redis Sentinel實(shí)現(xiàn)高可用配置的詳細(xì)步驟
這篇文章主要介紹了Redis Sentinel實(shí)現(xiàn)高可用配置的詳細(xì)步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09
Redis集群下過期key監(jiān)聽的實(shí)現(xiàn)代碼
這篇文章主要介紹了Redis集群下過期key監(jiān)聽的實(shí)現(xiàn)代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09
面試常問:如何保證Redis緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性
在實(shí)際開發(fā)過程中,緩存的使用頻率是非常高的,只要使用緩存和數(shù)據(jù)庫(kù)存儲(chǔ),就難免會(huì)出現(xiàn)雙寫時(shí)數(shù)據(jù)一致性的問題,那我們又該如何解決呢2021-09-09
redis緩存一致性延時(shí)雙刪代碼實(shí)現(xiàn)方式
這篇文章主要介紹了redis緩存一致性延時(shí)雙刪代碼實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08

