Redis數(shù)據(jù)結(jié)構(gòu)ZipList,QuickList,SkipList使用及說(shuō)明
1.ZipList
redis中的ZipList是一種緊湊的內(nèi)存儲(chǔ)存結(jié)構(gòu),主要可以節(jié)省內(nèi)存空間儲(chǔ)存小規(guī)模數(shù)據(jù)。是一種特殊的雙端鏈表,有一系列連續(xù)的內(nèi)存組成,可以在任意一端進(jìn)行壓入/彈出操作,而且操作的時(shí)間復(fù)雜度為O(1)
常見(jiàn)方法:
LPUSH mylist "value1" "value2" //左邊插入 RPUSH mylist "value3" "value4" //右邊插入 LPOP mylist //從鏈表最左端彈出數(shù)據(jù) RPOP mylist //從鏈表最右端彈出數(shù)據(jù) LRANGE mylist 0 2 //獲取左邊指定范圍的數(shù)據(jù) LINDEX mylist 0 //獲取指定下標(biāo)數(shù)據(jù) LLEN mylist //獲取列表長(zhǎng)度
| zlbytes | zltail | zllen | entry | ... | entry | entry | zlend |
| 屬性 | 類(lèi)型 | 長(zhǎng)度 | 用途 |
| zlbytes | uint32_t | 4字節(jié) | 記錄整個(gè)壓縮鏈表占用的內(nèi)存字節(jié)數(shù) |
| zltail | uint32_t | 4字節(jié) | 這個(gè)是偏移量,記錄壓縮列表尾節(jié)點(diǎn)到列表起始地址有多少字節(jié),通過(guò)這個(gè)偏移量,可以確定尾節(jié)點(diǎn)地址 |
| zllen | uint16_t | 2字節(jié) | 記錄列表包含的節(jié)點(diǎn)數(shù)量。最大65534,如果超出也只是65535 |
| entry | 列表節(jié)點(diǎn) | 不定 | 各個(gè)節(jié)點(diǎn)的長(zhǎng)度由節(jié)點(diǎn)保存的內(nèi)容確定 |
| zlend | uint8_t | 1字節(jié) | 特殊值0xFF(255),用于標(biāo)記壓縮例表的末端 |
1.2.解析Entry
ZipList的Entry不像普通鏈表那樣記錄前后節(jié)點(diǎn)的指針,因?yàn)橛涗浨昂蠊?jié)點(diǎn)指針需要16字節(jié),大大浪費(fèi)了內(nèi)存。所以采用下面的結(jié)構(gòu)來(lái)保存。
| previous_entry_length | encoding | content |
1.previous_entry_length:前一節(jié)點(diǎn)的長(zhǎng)度,占1字節(jié) 或 5字節(jié)
當(dāng)前一節(jié)點(diǎn)長(zhǎng)度 < 254字節(jié),采用 1字節(jié)保存這個(gè)長(zhǎng)度值
當(dāng)前一節(jié)點(diǎn)的長(zhǎng)度 > 254字節(jié),采用5字節(jié)保存這個(gè)長(zhǎng)度值
2.encoding:編碼屬性,記錄content的數(shù)據(jù)類(lèi)型(數(shù)字/字符串)及長(zhǎng)度,占用1,2,5字節(jié)。
3.contents:負(fù)責(zé)保存具體的內(nèi)容,可以是數(shù)字可以是字符串
注意:ZipList中所有儲(chǔ)存長(zhǎng)度的數(shù)值均采用小端字節(jié)序儲(chǔ)存,低位在前,高位字節(jié)在后。
列如:0x1234,采用小端字節(jié)序后就是 0x3412
1.3Encoding編碼
encoding編碼分為兩種:字符串和數(shù)字
字符串:encoding以00,01,10開(kāi)頭標(biāo)識(shí)儲(chǔ)存的是字符串。
下面長(zhǎng)度不同的字符串對(duì)應(yīng)encoding編碼格式

舉例:儲(chǔ)存字符串a(chǎn)b,bc


1.4.ZipList連鎖更新問(wèn)題
ZipList的每個(gè)Entry都包含previous_entry_length來(lái)記錄上一節(jié)點(diǎn)大小,長(zhǎng)度為1個(gè)或5字節(jié)。
>>如果前一節(jié)點(diǎn)長(zhǎng)度 < 254,那么采用1字節(jié)保存這個(gè)長(zhǎng)度值。
>>如果前一節(jié)點(diǎn) >= 254,則采用5字節(jié)保存這個(gè)長(zhǎng)度值。
如果有N個(gè)連續(xù)的長(zhǎng)度為250~253之間的entry,如果不巧有一個(gè)擴(kuò)展那么又可能發(fā)生連鎖反應(yīng),又可能所有都發(fā)生連鎖更新,新增,刪除都可能導(dǎo)致連鎖更新發(fā)生。
總結(jié):
1.壓縮列表可以看作連續(xù)內(nèi)存空間的“雙向鏈表“
2.列表的節(jié)點(diǎn)之間不是通過(guò)指針鏈接,而是上一節(jié)點(diǎn)和本節(jié)點(diǎn)長(zhǎng)度來(lái)尋址,大大節(jié)省內(nèi)存
3.如果數(shù)據(jù)過(guò)多,鏈表過(guò)長(zhǎng),可能影響查詢性能
4.增加刪除都可能發(fā)生連續(xù)更新問(wèn)題
2.QuickList
ZIpList雖然節(jié)省內(nèi)存,但是申請(qǐng)內(nèi)存必須是連續(xù)空間,如果內(nèi)存占用過(guò)多那么申請(qǐng)效率變低
數(shù)據(jù)量過(guò)大超出上限采用分片儲(chǔ)存數(shù)據(jù),那么這些ZipList如何聯(lián)系?
Redis3.2版本引入QuickList,是一個(gè)雙端鏈表,只不過(guò)每個(gè)節(jié)點(diǎn)都是ZipList

Redis提供配置:list-max-ziplist-size
如果值是正:代表ZipList允許的最大entry數(shù)。
如果值是負(fù):代表每個(gè)ZipList的最大內(nèi)存大小。
| -1 | -2 | -3 | -4 | -5 |
| 4kb | 8kb | 16kb | 32kb | 64kb |

QuickList可以控制首尾是否進(jìn)行壓縮,通過(guò)配置項(xiàng)list-compress-depth來(lái)控制,這個(gè)參數(shù)是控制首尾不壓縮的節(jié)點(diǎn)個(gè)數(shù)。
| 0 | 1 | 2 |
| 代表首尾節(jié)點(diǎn)不壓縮 | 首尾各有一個(gè)1個(gè)不壓縮,中間全壓縮 | 首尾各有2節(jié)點(diǎn)不壓縮,中間全壓縮 |

QuickList 和 Quick List Node的結(jié)構(gòu)源碼:

QuickList結(jié)構(gòu)圖:

QuickList特性:
1.每個(gè)節(jié)點(diǎn)都是ZipList的雙端鏈表
2.節(jié)點(diǎn)采用ZipList,解決傳統(tǒng)鏈表的內(nèi)存占用
3.控制ZipList大小,解決傳統(tǒng)連續(xù)內(nèi)存空間申請(qǐng)問(wèn)題
4.中間節(jié)點(diǎn)可壓縮,節(jié)省內(nèi)存
SkipList跳表
是個(gè)鏈表,但不是普通鏈表,不同節(jié)點(diǎn)之間跨度不一樣。
1.元素按照升序排列儲(chǔ)存
2.節(jié)點(diǎn)可能包含多個(gè)指針,指針跨度不同

為了更快的找到所需要的元素,SkipList采用這種結(jié)構(gòu)類(lèi)似于二分查找。
以下是結(jié)構(gòu)源碼:
// t_zset.c
typedef struct zskiplist {
// 頭尾節(jié)點(diǎn)指針
struct zskiplistNode* header, * tail;
// 節(jié)點(diǎn)數(shù)量
unsigned long length;
// 最大的索引層級(jí),默認(rèn)是1
int level;
} zskiplist;
// t_zset.c
typedef struct zskiplistNode {
sds ele; // 節(jié)點(diǎn)存儲(chǔ)的值
double score;// 節(jié)點(diǎn)分?jǐn)?shù),排序、查找用
struct zskiplistNode* backward; // 前一個(gè)節(jié)點(diǎn)指針
struct zskiplistLevel {
struct zskiplistNode* forward; // 下一個(gè)節(jié)點(diǎn)指針
unsigned long span; // 索引跨度
} level[]; // 多級(jí)索引數(shù)組
} zskiplistNode;
特性:
1.跳表是一個(gè)雙向鏈表,每個(gè)節(jié)點(diǎn)包含存儲(chǔ)的值和排序用的socre,用來(lái)保存別的節(jié)點(diǎn)的ele數(shù)組
2.節(jié)點(diǎn)按照score值排序,score值一樣按照ele字典排序
3.每層指針到下一節(jié)點(diǎn)跨度不同,層級(jí)越高跨度越大
4.增刪改查的效率與紅黑樹(shù)基本一致
RedisObject
在Redis中任意數(shù)據(jù)類(lèi)型的鍵和值都會(huì)被封裝在一個(gè)RedisObject中,也叫做Redis對(duì)象。

Redis會(huì)根據(jù)儲(chǔ)存不同的數(shù)據(jù)類(lèi)型選擇不同的編碼格式,共包含11種不同類(lèi)型。
| 編號(hào) | 編碼方式 | 說(shuō)明 |
|---|---|---|
| 0 | OBJ_ENCODING_RAW | 動(dòng)態(tài)字符串(動(dòng)態(tài)長(zhǎng)度,非預(yù)分配) |
| 1 | OBJ_ENCODING_INT | 長(zhǎng)整型(用 long 類(lèi)型存儲(chǔ)) |
| 2 | OBJ_ENCODING_HT | 哈希表(字典,基于 dict 實(shí)現(xiàn)) |
| 3 | OBJ_ENCODING_ZIPMAP | 已廢棄的哈希壓縮結(jié)構(gòu) |
| 4 | OBJ_ENCODING_LINKEDLIST | 雙端鏈表(舊版 List 實(shí)現(xiàn)) |
| 5 | OBJ_ENCODING_ZIPLIST | 壓縮列表(緊湊的二進(jìn)制結(jié)構(gòu)) |
| 6 | OBJ_ENCODING_INTSET | 整數(shù)集合(僅存儲(chǔ)整數(shù)的有序集合) |
| 7 | OBJ_ENCODING_SKIPLIST | 跳表(有序集合的底層實(shí)現(xiàn)之一) |
| 8 | OBJ_ENCODING_EMBSTR | 短字符串(固定長(zhǎng)度,預(yù)分配內(nèi)存) |
| 9 | OBJ_ENCODING_QUICKLIST | 快速列表(雙向鏈表 + 壓縮列表) |
| 10 | OBJ_ENCODING_STREAM | Stream 流(消息隊(duì)列結(jié)構(gòu)) |
五種數(shù)據(jù)類(lèi)型
String基于簡(jiǎn)單動(dòng)態(tài)字符串SDS實(shí)現(xiàn),存儲(chǔ)上限為512mb,基本編碼方式是RAM
List可以從首尾操作列表中的元素,3.2版本后統(tǒng)一采用QuickList來(lái)實(shí)現(xiàn)List
Set:
1.為了查詢效率和唯一性,采用Dict編碼,key為存儲(chǔ)元素,value統(tǒng)一為null
2.所有數(shù)據(jù)都是整數(shù),且元素?cái)?shù)量不超過(guò)set-max-intset-entries,Set會(huì)采用IntSet編碼

ZSet:
ZSet就是SortedSet,其中每個(gè)元素都需指定一個(gè)score 和 member值
可以根據(jù)score排序,且member必須唯一,可以根據(jù)member查詢分?jǐn)?shù)
當(dāng)元素?cái)?shù)數(shù)量小時(shí),采用ZipList來(lái)節(jié)省內(nèi)存,但是需要滿足兩個(gè)條件:
1.元素?cái)?shù)量小于 zset_max_ziplist_entries (128)
2.每個(gè)元素都小于zset_max_ziplist_value(64)

當(dāng)數(shù)據(jù)量大時(shí)采用SkipList和HT結(jié)構(gòu)

Hash:
hash底層的編碼和ZSet基本一致,只需要把排序有關(guān)的SkipList去掉就行。
1.數(shù)據(jù)量小時(shí),采用ZipList編碼節(jié)省內(nèi)存,ZipList中相鄰的兩個(gè)entry保存field和value

2.數(shù)據(jù)量大時(shí),采用HT編碼,就是Dict,觸發(fā)條件是:元素?cái)?shù)量超過(guò)512,每個(gè)entry超過(guò)64字節(jié)

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Redis中3種特殊的數(shù)據(jù)類(lèi)型(BitMap、Geo和HyperLogLog)
這篇文章主要給大家介紹了關(guān)于Redis中3種特殊的數(shù)據(jù)類(lèi)型(BitMap、GEOADD和GEODIST)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03
Redis SETEX命令實(shí)現(xiàn)鍵值對(duì)管理
本文主要介紹了Redis SETEX命令實(shí)現(xiàn)鍵值對(duì)管理,SETEX命令用于設(shè)置具有過(guò)期時(shí)間的鍵值對(duì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
關(guān)于Redis未授權(quán)訪問(wèn)的問(wèn)題
這篇文章主要介紹了Redis未授權(quán)訪問(wèn)的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07
分布式爬蟲(chóng)處理Redis里的數(shù)據(jù)操作步驟
這篇文章主要介紹了分布式爬蟲(chóng)處理Redis里的數(shù)據(jù)操作步驟,數(shù)據(jù)分別存入mongodb和mysql數(shù)據(jù)庫(kù),具體內(nèi)容詳情及實(shí)例代碼大家參考下本文2018-03-03
redis字符串類(lèi)型_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了redis字符串類(lèi)型的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08

