redis的zset的底层实现_Redis(三)--- Redis的五大数据类型的底层实现
1、簡(jiǎn)介
Redis的五大數(shù)據(jù)類型也稱五大數(shù)據(jù)對(duì)象;前面介紹過(guò)6大數(shù)據(jù)結(jié)構(gòu),Redis并沒(méi)有直接使用這些結(jié)構(gòu)來(lái)實(shí)現(xiàn)鍵值對(duì)數(shù)據(jù)庫(kù),而是使用這些結(jié)構(gòu)構(gòu)建了一個(gè)對(duì)象系統(tǒng)redisObject;這個(gè)對(duì)象系統(tǒng)包含了五大數(shù)據(jù)對(duì)象,字符串對(duì)象(string)、列表對(duì)象(list)、哈希對(duì)象(hash)、集合(set)對(duì)象和有序集合對(duì)象(zset);而這五大對(duì)象的底層數(shù)據(jù)編碼可以用命令OBJECT ENCODING來(lái)進(jìn)行查看。
redisObject結(jié)構(gòu)
1 typedef structredisObject {2 //類型
3 unsigned type:4;4 //編碼
5 unsigned encoding:4;6 //指向底層實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)的指針
7 void *ptr;8 //...
9 } robj;
redis是以鍵值對(duì)存儲(chǔ)數(shù)據(jù)的,所以對(duì)象又分為鍵對(duì)象和值對(duì)象,即存儲(chǔ)一個(gè)key-value鍵值對(duì)會(huì)創(chuàng)建兩個(gè)對(duì)象,鍵對(duì)象和值對(duì)象。
鍵對(duì)象總是一個(gè)字符串對(duì)象,而值對(duì)象可以是五大對(duì)象中的任意一種。
type屬性存儲(chǔ)的是對(duì)象的類型,也就是我們說(shuō)的?string、list、hash、set、zset中的一種,可以使用命令 TYPE key?來(lái)查看。
encoding屬性記錄了隊(duì)形所使用的編碼,即這個(gè)對(duì)象底層使用哪種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)。
表中列出了底層編碼常量及對(duì)應(yīng)的OBJECT ENCODING?命令的輸出,前三項(xiàng)都是字符串結(jié)構(gòu)
我們?cè)诖嫒雓ey-value鍵值對(duì)時(shí)并不會(huì)指定對(duì)象的encoding,而是Redis會(huì)根據(jù)不統(tǒng)的使用場(chǎng)景來(lái)為一個(gè)對(duì)象設(shè)置不同的編碼,可以達(dá)到節(jié)約內(nèi)存、加快訪問(wèn)速度等目的。
2、字符串對(duì)象(string)
字符串對(duì)象底層數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)為簡(jiǎn)單動(dòng)態(tài)字符串(SDS)和直接存儲(chǔ),但其編碼方式可以是int、raw或者embstr,區(qū)別在于內(nèi)存結(jié)構(gòu)的不同。
(1)int編碼
字符串保存的是整數(shù)值,并且這個(gè)正式可以用long類型來(lái)表示,那么其就會(huì)直接保存在redisObject的ptr屬性里,并將編碼設(shè)置為int,如圖:
(2)raw編碼
字符串保存的大于32字節(jié)的字符串值,則使用簡(jiǎn)單動(dòng)態(tài)字符串(SDS)結(jié)構(gòu),并將編碼設(shè)置為raw,此時(shí)內(nèi)存結(jié)構(gòu)與SDS結(jié)構(gòu)一致,內(nèi)存分配次數(shù)為兩次,創(chuàng)建redisObject對(duì)象和sdshdr結(jié)構(gòu),如圖:
(3)embstr編碼
字符串保存的小于等于32字節(jié)的字符串值,使用的也是簡(jiǎn)單的動(dòng)態(tài)字符串(SDS結(jié)構(gòu)),但是內(nèi)存結(jié)構(gòu)做了優(yōu)化,用于保存頓消的字符串;內(nèi)存分配也只需要一次就可完成,分配一塊連續(xù)的空間即可,如圖:
字符串對(duì)象總結(jié):
在Redis中,存儲(chǔ)long、double類型的浮點(diǎn)數(shù)是先轉(zhuǎn)換為字符串再進(jìn)行存儲(chǔ)的。
raw與embstr編碼效果是相同的,不同在于內(nèi)存分配與釋放,raw兩次,embstr一次。
embstr內(nèi)存塊連續(xù),能更好的利用緩存在來(lái)的優(yōu)勢(shì)
int編碼和embstr編碼如果做追加字符串等操作,滿足條件下會(huì)被轉(zhuǎn)換為raw編碼;embstr編碼的對(duì)象是只讀的,一旦修改會(huì)先轉(zhuǎn)碼到raw。
3、列表對(duì)象(list)
列表對(duì)象的編碼可以是ziplist和linkedlist之一。
(1)?ziplist編碼
ziplist編碼的哈希隨想底層實(shí)現(xiàn)是壓縮列表,每個(gè)壓縮里列表節(jié)點(diǎn)保存了一個(gè)列表元素。
(2)linkedlist編碼
linkedlist編碼底層采用雙端鏈表實(shí)現(xiàn),每個(gè)雙端鏈表節(jié)點(diǎn)都保存了一個(gè)字符串對(duì)象,在每個(gè)字符串對(duì)象內(nèi)保存了一個(gè)列表元素。
列表對(duì)象編碼轉(zhuǎn)換:
列表對(duì)象使用ziplist編碼需要滿足兩個(gè)條件:一是所有字符串長(zhǎng)度都小于64字節(jié),二是元素?cái)?shù)量小于512,不滿足任意一個(gè)都會(huì)使用linkedlist編碼。
兩個(gè)條件的數(shù)字可以在Redis的配置文件中修改,list-max-ziplist-value選項(xiàng)和list-max-ziplist-entries選項(xiàng)。
圖中StringObject就是上一節(jié)講到的字符串對(duì)象,字符串對(duì)象是唯一個(gè)在五大對(duì)象中作為嵌套對(duì)象使用的。
4、哈希對(duì)象(hash)
哈希對(duì)象的編碼可以是ziplist和hashtable之一。
(1)ziplist編碼
ziplist編碼的哈希對(duì)象底層實(shí)現(xiàn)是壓縮列表,在ziplist編碼的哈希對(duì)象中,key-value鍵值對(duì)是以緊密相連的方式放入壓縮鏈表的,先把key放入表尾,再放入value;鍵值對(duì)總是向表尾添加。
(2)hashtable編碼
hashtable編碼的哈希對(duì)象底層實(shí)現(xiàn)是字典,哈希對(duì)象中的每個(gè)key-value對(duì)都使用一個(gè)字典鍵值對(duì)來(lái)保存。
字典鍵值對(duì)即是,字典的鍵和值都是字符串對(duì)象,字典的鍵保存key-value的key,字典的值保存key-value的value。
哈希對(duì)象編碼轉(zhuǎn)換:
哈希對(duì)象使用ziplist編碼需要滿足兩個(gè)條件:一是所有鍵值對(duì)的鍵和值的字符串長(zhǎng)度都小于64字節(jié);二是鍵值對(duì)數(shù)量小于512個(gè);不滿足任意一個(gè)都使用hashtable編碼。
以上兩個(gè)條件可以在Reids配置文件中修改hash-max-ziplist-value選項(xiàng)和hash-max-ziplist-entries選項(xiàng)。
5、集合對(duì)象(set)
集合對(duì)象的編碼可以是intset和hashtable之一。
(1)intset編碼
intset編碼的集合對(duì)象底層實(shí)現(xiàn)是整數(shù)集合,所有元素都保存在整數(shù)集合中。
(2)hashtable編碼
hashtable編碼的集合對(duì)象底層實(shí)現(xiàn)是字典,字典的每個(gè)鍵都是一個(gè)字符串對(duì)象,保存一個(gè)集合元素,不同的是字典的值都是NULL;可以參考java中的hashset結(jié)構(gòu)。
集合對(duì)象編碼轉(zhuǎn)換:
集合對(duì)象使用intset編碼需要滿足兩個(gè)條件:一是所有元素都是整數(shù)值;二是元素個(gè)數(shù)小于等于512個(gè);不滿足任意一條都將使用hashtable編碼。
以上第二個(gè)條件可以在Redis配置文件中修改et-max-intset-entries選項(xiàng)。
6、有序集合對(duì)象(zset)
有序集合的編碼可以是ziplist和skiplist之一。
(1)ziplist編碼
ziplist編碼的有序集合對(duì)象底層實(shí)現(xiàn)是壓縮列表,其結(jié)構(gòu)與哈希對(duì)象類似,不同的是兩個(gè)緊密相連的壓縮列表節(jié)點(diǎn),第一個(gè)保存元素的成員,第二個(gè)保存元素的分值,而且分值小的靠近表頭,大的靠近表尾。
(2)skiplist編碼
skiplist編碼的有序集合對(duì)象底層實(shí)現(xiàn)是跳躍表和字典兩種;
每個(gè)跳躍表節(jié)點(diǎn)都保存一個(gè)集合元素,并按分值從小到大排列;節(jié)點(diǎn)的object屬性保存了元素的成員,score屬性保存分值;
字典的每個(gè)鍵值對(duì)保存一個(gè)集合元素,字典的鍵保存元素的成員,字典的值保存分值。
為何skiplist編碼要同時(shí)使用跳躍表和字典實(shí)現(xiàn)?
跳躍表優(yōu)點(diǎn)是有序,但是查詢分值復(fù)雜度為O(logn);字典查詢分值復(fù)雜度為O(1) ,但是無(wú)序,所以結(jié)合連個(gè)結(jié)構(gòu)的有點(diǎn)進(jìn)行實(shí)現(xiàn)。
雖然采用兩個(gè)結(jié)構(gòu)但是集合的元素成員和分值是共享的,兩種結(jié)構(gòu)通過(guò)指針指向同一地址,不會(huì)浪費(fèi)內(nèi)存。
有序集合編碼轉(zhuǎn)換:
有序集合對(duì)象使用ziplist編碼需要滿足兩個(gè)條件:一是所有元素長(zhǎng)度小于64字節(jié);二是元素個(gè)數(shù)小于128個(gè);不滿足任意一條件將使用skiplist編碼。
以上兩個(gè)條件可以在Redis配置文件中修改zset-max-ziplist-entries選項(xiàng)和zset-max-ziplist-value選項(xiàng)。
7、總結(jié)
在Redis的五大數(shù)據(jù)對(duì)象中,string對(duì)象是唯一個(gè)可以被其他四種數(shù)據(jù)對(duì)象作為內(nèi)嵌對(duì)象的;
列表(list)、哈希(hash)、集合(set)、有序集合(zset)底層實(shí)現(xiàn)都用到了壓縮列表結(jié)構(gòu),并且使用壓縮列表結(jié)構(gòu)的條件都是在元素個(gè)數(shù)比較少、字節(jié)長(zhǎng)度較短的情況下;
四種數(shù)據(jù)對(duì)象使用壓縮列表的優(yōu)點(diǎn):
(1)節(jié)約內(nèi)存,減少內(nèi)存開(kāi)銷,Redis是內(nèi)存型數(shù)據(jù)庫(kù),所以一定情況下減少內(nèi)存開(kāi)銷是非常有必要的。
(2)減少內(nèi)存碎片,壓縮列表的內(nèi)存塊是連續(xù)的,并分配內(nèi)存的次數(shù)一次即可。
(3)壓縮列表的新增、刪除、查找操作的平均時(shí)間復(fù)雜度是O(N),在N再一定的范圍內(nèi),這個(gè)時(shí)間幾乎是可以忽略的,并且N的上限值是可以配置的。
(4)四種數(shù)據(jù)對(duì)象都有兩種編碼結(jié)構(gòu),靈活性增加。
參考:
《Redis設(shè)計(jì)與實(shí)現(xiàn)》黃健宏著,網(wǎng)上對(duì)Redis的詳解等
此博客為筆者使用redis很久之后,參考網(wǎng)絡(luò)上各類文章總結(jié)性書寫,原創(chuàng)手打,如有錯(cuò)誤歡迎指正。
總結(jié)
以上是生活随笔為你收集整理的redis的zset的底层实现_Redis(三)--- Redis的五大数据类型的底层实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 在linux中用高斯09优化分子结构,高
- 下一篇: Linux源码安装mysql 5.6.1