日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

承接上文redis底層字符串編碼設(shè)計(jì)思想簡(jiǎn)介

ziplist數(shù)據(jù)結(jié)構(gòu)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

內(nèi)存會(huì)一次性開(kāi)辟一塊大的連續(xù)的空間,來(lái)存放ziplist。

  • • zlbytes

32bit內(nèi)存空間,表示ziplist占用的字節(jié)總數(shù)

  • • zltail

32bit內(nèi)存空間,表示ziplist表中最后一項(xiàng)(entry)在ziplist中的偏移字節(jié)數(shù),通過(guò)zltail可以很方便的找到最后一項(xiàng),從而可以在ziplist尾端快速的執(zhí)行push或pop操作。

  • • zllen

16bit內(nèi)存空間,表示ziplist中數(shù)據(jù)項(xiàng)entry的個(gè)數(shù)

  • • zlend:255

ziplist最后一個(gè)字節(jié),是一個(gè)結(jié)束標(biāo)記,值固定等于255

  • • entry

表示真正存放數(shù)據(jù)的數(shù)據(jù)項(xiàng),長(zhǎng)度不定

  • • prerawlen

表示上一個(gè)節(jié)點(diǎn)的數(shù)據(jù)長(zhǎng)度信息。

如果前一個(gè)元素的大小prerawlen等于254,用一個(gè)字節(jié)作為標(biāo)記項(xiàng),在用4個(gè)字節(jié)描述前一個(gè)元素大小。

ziplist額外的信息最多需要5個(gè)字節(jié)存儲(chǔ),相比quicklist雙端鏈表的一個(gè)元素需要2個(gè)指針16個(gè)字節(jié)來(lái)說(shuō)節(jié)省很多內(nèi)存開(kāi)銷(xiāo)。

  • • len

entry中數(shù)據(jù)的長(zhǎng)度

  • • data

表示當(dāng)前元素里面的真實(shí)數(shù)據(jù),業(yè)務(wù)數(shù)據(jù)存儲(chǔ),這是一個(gè)非常緊湊的二進(jìn)制數(shù)據(jù)結(jié)構(gòu)。

連續(xù)的內(nèi)存空間也會(huì)存在弊端?

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

ziplist放入n多個(gè)元素的時(shí)候,再往里面添加元素,都需要分配新的內(nèi)存空間,并且完成數(shù)據(jù)的完整拷貝。元素比較少還好,但如果元素很多的情況下,會(huì)很消耗性能。

那么就需要借助雙端鏈表來(lái)控制ziplist的大小,如果超過(guò)一定的大小,則分裂成兩個(gè),保證每個(gè)ziplist不能太大。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

quicklist的底層是quicklistNode,quicklistNode指向ziplist。

加一些數(shù)據(jù),比如加入到ziplist里面去,不需要對(duì)數(shù)據(jù)整個(gè)做偏移,因?yàn)閿?shù)據(jù)已經(jīng)分配到不同的ziplist里面去了,修改數(shù)據(jù),只需要修改某一個(gè)ziplist就可以了。

這是基于雙端鏈表做的優(yōu)化,可以從前往后遍歷,也可以從后往前遍歷。

接下來(lái)看下lpush源碼

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

pushGenericCommand

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

先從redis數(shù)據(jù)庫(kù)獲取數(shù)據(jù)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • c->db

db就是要操作的redis數(shù)據(jù)庫(kù)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • c->argv[1]

比如執(zhí)行l(wèi)push命令

lpush alist a b c

argv[0]就是lpush
argv[1]就是alist

根據(jù)key去db中查找數(shù)據(jù)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • key->ptr

key真正所執(zhí)行的字符串

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

進(jìn)行rehash操作

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

-1表示沒(méi)有進(jìn)行rehash,不等于-1表示正在進(jìn)行rehash。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

做rehash的方法

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

篩選非空的hash槽

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

有時(shí)候,hash槽上不一定有數(shù)據(jù),因?yàn)閔ashtable散列之后有的是空的,如果循環(huán)的時(shí)候發(fā)現(xiàn)有empty_visits=10個(gè)hash槽是空的,就不做rehash了。

這個(gè)while循環(huán)結(jié)束之后,剩下的都是非空的hash槽。

獲取非空hash槽上鏈表的頭節(jié)點(diǎn)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

開(kāi)始遍歷鏈表,重新計(jì)算hash值。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

這里是進(jìn)行與運(yùn)算,而不是求模運(yùn)算,性能更高。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • size

size大小為2^n

  • • sizemask

sizemask=size-1=2^n-1

公式

任意數(shù) % 2^n <=> 任意數(shù) & (2^n-1)

即累除法求余數(shù),一次位運(yùn)算就能計(jì)算出結(jié)果,更加高效。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

計(jì)算出key在新的hashtable上的位置之后,將老的hash槽索引指向新的hashtable的表頭,并將老的hash槽上的鏈表遷移到新的hashtable上去,完成了數(shù)據(jù)遷移操作。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

遷移過(guò)來(lái)之后,老的hashtable上的元素個(gè)數(shù)減1,新的加1。

一個(gè)hash槽一個(gè)hash槽的遷移數(shù)據(jù)。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

判斷老的hashtable中還有沒(méi)有元素,如果沒(méi)有的話,就釋放掉老的hashtable。

將老的hashtable(ht[0])指向新的hashtable(ht[1]),新的hashtable(ht[1])就釋放掉了。

至此,就完成了rehash的過(guò)程。

rehash完之后,接下來(lái)就是查找key的過(guò)程

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

先從老的hashtable查找(ht[0]),有的話就返回,沒(méi)有的話再查找新的hashtable(ht[1]),還沒(méi)有的話就返回null。

接下來(lái)就是檢查查詢(xún)到數(shù)據(jù)的類(lèi)型

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

看是否為L(zhǎng)ist數(shù)據(jù)類(lèi)型

lpush alist a b c

如果alist不是List而是string 則會(huì)報(bào)異常提示類(lèi)型不匹配

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

如果在db中沒(méi)有找到數(shù)據(jù),則創(chuàng)建一個(gè)quicklist

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

list數(shù)據(jù)類(lèi)型,底層編碼是quicklist。

接下來(lái)就是把創(chuàng)建好的數(shù)據(jù)添加到字典中了

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

quicklistSetOptions

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

設(shè)置每個(gè)ziplist的最大容量,quicklist的數(shù)據(jù)壓縮范圍,提升數(shù)據(jù)的存取效率

  • • list-max-ziplist-size -2

單個(gè)ziplist節(jié)點(diǎn)最大能存儲(chǔ)8kb,超過(guò)則進(jìn)行分裂,將數(shù)據(jù)存儲(chǔ)在新的ziplist節(jié)點(diǎn)中

默認(rèn)8kb的數(shù)據(jù)可以分鏈

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

不推薦-5,因?yàn)閿?shù)據(jù)大了往內(nèi)存中添加元素,會(huì)涉及到數(shù)據(jù)的拷貝,導(dǎo)致性能降低。

  • • list-compress-depth 1
Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

0代表所有節(jié)點(diǎn),都不進(jìn)行壓縮,1代表從頭節(jié)點(diǎn)往后走一個(gè),尾節(jié)點(diǎn)往前走一個(gè)不用壓縮,其他的全部壓縮,2,3,4以此類(lèi)推

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

雙端鏈表內(nèi)存是不連續(xù)的,就會(huì)導(dǎo)致內(nèi)存碎片問(wèn)題,壓縮了之后,就可以做成整塊的內(nèi)存空間了,使得數(shù)據(jù)更加的有規(guī)律,節(jié)省了內(nèi)存空間。

dbAdd

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

每次訪問(wèn)hashtable都會(huì)做一次rehash的操作。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

rehash完之后,求索引值

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

存放元素:如果正在做rehash,則將新增的元素放入新的hashtable中

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

采用頭插法,最新添加的,會(huì)被更頻繁的訪問(wèn)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

老的hashtable索引指向新創(chuàng)建的元素地址,entry就是新創(chuàng)建的節(jié)點(diǎn)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

hashtable指向的就是鏈表的頭部節(jié)點(diǎn)

接下來(lái)把添加的數(shù)據(jù)追加到list中去

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

創(chuàng)建quicklistNode和ziplist

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

Set 數(shù)據(jù)類(lèi)型

set語(yǔ)義上來(lái)說(shuō)是無(wú)序的

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

上面這種整數(shù)的、元素不多的是通過(guò)有序的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

基于上面都是整數(shù)元素的基礎(chǔ)上添加一個(gè)字符串元素,返回結(jié)果是1,因?yàn)橹惶砑舆M(jìn)去一個(gè)元素。

此時(shí)的編碼是hashtable,因?yàn)樽址當(dāng)?shù)據(jù)的那個(gè)元素沒(méi)有辦法用整型值編碼的時(shí)候,會(huì)重新轉(zhuǎn)換成一個(gè)hashtable。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

此時(shí)就變成無(wú)序的了,因?yàn)閔ashtable本身就是無(wú)序的數(shù)據(jù)結(jié)構(gòu)。

Set為無(wú)序,自動(dòng)去重的集合數(shù)據(jù)類(lèi)型,Set數(shù)據(jù)結(jié)構(gòu)底層實(shí)現(xiàn)為一個(gè)value為null的字典(dict),當(dāng)數(shù)據(jù)可以用整型表示時(shí),Set集合將被編碼為intset數(shù)據(jù)結(jié)構(gòu)。以下兩個(gè)條件任意滿足一個(gè),Set將用hashtable存儲(chǔ)數(shù)據(jù)

  • • 元素個(gè)數(shù)大于set-max-intset-entries

set-max-intset-entries 512 intset能存儲(chǔ)的最大元素個(gè)數(shù),超過(guò)則用hashtable編碼

  • • 元素?zé)o法用整型表示

intset就是一個(gè)數(shù)組,有序,都是整形元素。通過(guò)二分查找來(lái)判斷某個(gè)元素是否存在。用整型數(shù)組更節(jié)省空間。

sadd源碼

從server.c 文件中的 redisCommand redis命令集合中找到sadd

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

查詢(xún)r(jià)edis db中是否存在set集合

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

如果不存在的話,則創(chuàng)建set集合

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

判斷字符串是否可以轉(zhuǎn)換成long,如果可以則創(chuàng)建intset

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

創(chuàng)建intset集合

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

數(shù)據(jù)類(lèi)型是set,數(shù)據(jù)編碼是intset

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

否則創(chuàng)建hashtable

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

set集合創(chuàng)建好之后,將set添加到db中

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

dictAddRaw

如果數(shù)據(jù)編碼是hashtable

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

值設(shè)置為NULL

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

intset

如果是數(shù)字,則追加到intset中去

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

intsetAdd

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

intsetValueEncoding

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

判斷下數(shù)據(jù)范圍,3種類(lèi)型的數(shù)組,16個(gè)bit位的,32bit位,64bit位的,判斷下追加的值屬于哪個(gè)范圍,就用對(duì)應(yīng)的數(shù)組,這是為了節(jié)省內(nèi)存空間,根據(jù)數(shù)據(jù)的范圍去創(chuàng)建不同的數(shù)組,如果數(shù)據(jù)量非常大的話,就用大一點(diǎn)的位數(shù)去創(chuàng)建數(shù)組。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

如果范圍比現(xiàn)有的范圍還大的話,就進(jìn)行升級(jí),如果超過(guò)了32位就用64位,超過(guò)了16位,就用32位的數(shù)組。

如果小于現(xiàn)有的編碼長(zhǎng)度的話

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

查詢(xún)下數(shù)據(jù)在當(dāng)前intset中是否存在,intset也會(huì)自動(dòng)去重,已經(jīng)存在的話,就直接返回。

intsetSearch

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

這里使用的是二分查找,break就是終止了,然后判斷是否存在

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

往右移動(dòng)一位就是除以2

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

相等就是存在,否則就是不存在,不存在的話,就記錄下position,這樣就知道下一次數(shù)據(jù)放在哪個(gè)位置了。

因?yàn)橐砑釉兀砸匦聰U(kuò)容

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

擴(kuò)容之后,在對(duì)應(yīng)的position上把添加的元素放進(jìn)去

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

根據(jù)數(shù)據(jù)的長(zhǎng)度采用不同的存儲(chǔ)類(lèi)型來(lái)存放

Set 數(shù)據(jù)類(lèi)型底層intset編碼

整數(shù)集合是一個(gè)有序的,存儲(chǔ)整型數(shù)據(jù)的結(jié)構(gòu),整型集合在redis中可以保存int16_t,int32_t,int64_t類(lèi)型的整型數(shù)據(jù),并且可以保證集合中不會(huì)出現(xiàn)重復(fù)數(shù)據(jù)。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

Hash 數(shù)據(jù)類(lèi)型

Hash數(shù)據(jù)結(jié)構(gòu)底層實(shí)現(xiàn)是一個(gè)字典(dict),也是RedisDB用來(lái)存儲(chǔ)K-V的數(shù)據(jù)結(jié)構(gòu),當(dāng)數(shù)據(jù)量比較小,或者單個(gè)元素比較小時(shí),底層用ziplist存儲(chǔ)。數(shù)據(jù)大小和元素閾值可以通過(guò)如下參數(shù)設(shè)置:

  • • hash-max-ziplist-entries 512

ziplist元素個(gè)數(shù)超過(guò)512,將改為hashtable編碼

  • • hash-max-ziplist-value 64

單個(gè)元素大小超過(guò)64byte時(shí),將改為hashtable編碼

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

按照放入順序存取

Hash數(shù)據(jù)類(lèi)型ziplist編碼

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

用hash去存儲(chǔ)K-V的時(shí)候,比如name、age,一個(gè)鍵值對(duì)拆成2個(gè)元素放到一個(gè)ziplist里面,從而更有效的利用內(nèi)存。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

單個(gè)元素超過(guò)64字節(jié),就會(huì)變成無(wú)序的hashtable編碼存儲(chǔ)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

有序的ZSet

zset/sorted_set數(shù)據(jù)類(lèi)型

ZSet是有序的,自動(dòng)去重的集合數(shù)據(jù)類(lèi)型,ZSet數(shù)據(jù)結(jié)構(gòu)底層實(shí)現(xiàn)為字典(dict)+跳表(skiplist),當(dāng)數(shù)據(jù)比較少時(shí),用ziplist編碼結(jié)構(gòu)存儲(chǔ)

  • • zset-max-ziplist-entries 128

元素個(gè)數(shù)超過(guò)128,將用skiplist編碼

  • • zset-max-ziplist-value 64

單個(gè)元素超過(guò)64byte,將用skiplist

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

0,2表示排名;withscores表示分值打印

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

當(dāng)數(shù)據(jù)量比較小的時(shí)候,編碼是緊湊的連續(xù)空間,對(duì)內(nèi)存利用率高,可自動(dòng)去重。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

當(dāng)數(shù)據(jù)量比較大的時(shí)候,底層編碼就變成了skiplist 跳表

skiplist 跳表數(shù)據(jù)結(jié)構(gòu)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

這是一個(gè)有序的鏈表,想要找元素79,挨個(gè)遍歷才能找到,時(shí)間復(fù)雜度是O(n)。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

在數(shù)據(jù)層基礎(chǔ)之上提一個(gè)索引層出來(lái),每隔一個(gè)元素就提一個(gè)索引出來(lái),用索引幫助查詢(xún)數(shù)據(jù),在索引層直到找到比79大的數(shù)據(jù),就不往后找了即索引層找到78,下沉到數(shù)據(jù)層78,再往后找一個(gè)就是79了,這樣就會(huì)跳過(guò)很多的元素,提高查詢(xún)效率。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

再提一個(gè)索引層2

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

再提一個(gè)索引層3

時(shí)間復(fù)雜度分析

數(shù)據(jù): n

index 1 n/2

index 2 n/2^2

index 3 n/2^3

index k n/2^k

每添加一個(gè)索引層就減少一半的數(shù)據(jù)量即索引第三層是索引第二層的一半,索引第二層是索引第一層的一半,索引第一層是數(shù)據(jù)層的一半。

每一層都只需要常數(shù)級(jí)別查詢(xún)次數(shù)即可,該查詢(xún)次數(shù)就是層高。

根據(jù)數(shù)據(jù)歸納法,假設(shè)索引層最頂層有2個(gè)數(shù)據(jù),即n/2^k=2 => 2^k=n/2 => k= log_2 n

以2為底,n的對(duì)數(shù)即log n的時(shí)間復(fù)雜度。

zadd源碼

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

查詢(xún)zset,如果沒(méi)有,則創(chuàng)建一個(gè)跳表或zskiplist

  • • 創(chuàng)建跳表zskiplist
Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • 創(chuàng)建ziplist
Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

zset是復(fù)合型數(shù)據(jù)結(jié)構(gòu):字典+跳表

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

字典可以最快速的索引到記錄,跳表存儲(chǔ)的是有序的數(shù)據(jù)。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

zscore O(1)的時(shí)間復(fù)雜度,通過(guò)元素拿到分值,查詢(xún)效率高效是源于zset的dict字典數(shù)據(jù)結(jié)構(gòu)。

dict里面存儲(chǔ)的是索引,不是真正的值,真正的值只有一份,是由dict和zskiplist共享,盡量減少存儲(chǔ)空間的前提下提升訪問(wèn)速度。

zskiplist

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • skiplistNode *header, *tail

雙向指針

1、可以從前往后遍歷

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

升序

2、也可以從后往前遍歷

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

倒序

  • • long length

元素個(gè)數(shù)

  • • int level

索引最高的層高。在redis中索引層的索引沒(méi)有那么規(guī)律,而是用隨機(jī)數(shù)生成的。

zskiplistNode

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • sds ele 元素
  • • double score 分值
  • • zskiplistNode *backward; 同類(lèi)型指針,指向后面的節(jié)點(diǎn)
  • • zskiplistLevel level[] 索引層 是個(gè)數(shù)組
  • • zskiplistLevel.zskiplistNode 指向前面的節(jié)點(diǎn)
  • • long span 兩個(gè)索引層之間的間隔
Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

zskiplist數(shù)據(jù)結(jié)構(gòu)補(bǔ)充說(shuō)明

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • header指向頭結(jié)點(diǎn),頭結(jié)點(diǎn)的層高始終為64層
  • • level保存跳表中節(jié)點(diǎn)層數(shù)最高值,但是不計(jì)算頭結(jié)點(diǎn)的層高
  • • length保存跳表中節(jié)點(diǎn)個(gè)數(shù),但是不包括頭結(jié)點(diǎn)
  • • tail指向尾節(jié)點(diǎn)

舉例說(shuō)明:

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

假設(shè)要找score為5的元素

(1)首先從頭結(jié)點(diǎn)的最高層(也就是64層)開(kāi)始查找,但是由于頭指針中保存了當(dāng)前跳表的最高層數(shù),所以直接從頭結(jié)點(diǎn)的level層開(kāi)始查找即可。如果zkiplistLevel的forward為NULL,則繼續(xù)向下

(2)直到頭結(jié)點(diǎn)中第5層的zskiplistLevel的forward不為空,它指向了第6個(gè)節(jié)點(diǎn)的第5層,但是這個(gè)節(jié)點(diǎn)的score為6,大于5,所以還要從頭結(jié)點(diǎn)向低層繼續(xù)查找

(3)頭結(jié)點(diǎn)中第4層的zskiplistLevel的forward指向第3個(gè)節(jié)點(diǎn)的第4層,這個(gè)節(jié)點(diǎn)的score為3,小于4,繼續(xù)從這個(gè)節(jié)點(diǎn)的forward向后遍歷。它的forward指向第6個(gè)節(jié)點(diǎn)的第4層,這個(gè)節(jié)點(diǎn)的score為6,大于5,所以還要從前一個(gè)score為3的節(jié)點(diǎn)向低層繼續(xù)查找

(4)第3個(gè)節(jié)點(diǎn)第3層的zskiplistLevel的forward指向第5個(gè)節(jié)點(diǎn)的第3層,而它的score正好為5,查找成功

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

求元素79的排名,每經(jīng)過(guò)一個(gè)節(jié)點(diǎn)就計(jì)算它之間的span,就能算出它的排名。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

將生成好的zset放入db中

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

先rehash,然后存儲(chǔ)在hashtable中去。

然后就是計(jì)算分值并往zset中添加元素

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • 往ziplist添加元素
Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

  • • 往跳表中添加元素
Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

從跳表字典中查找記錄

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

時(shí)間復(fù)雜度是O(1)。

如果de不為空,說(shuō)明元素之前已存在了,此時(shí)如果是setnx,則會(huì)直接抱異常,否則可以修改它的值。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

如果兩個(gè)值不相等,先刪除再插入數(shù)據(jù)。

zslUpdateScore

Redis中的做法比較簡(jiǎn)單,先找到待修改的節(jié)點(diǎn),直接刪除,然后插入修改后的新的節(jié)點(diǎn)。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

找新增元素的位置:遍歷所有的索引層,找位置。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

判斷分值在什么地方,如果在原來(lái)元素的位置,不用移動(dòng)元素,更新分值即可。

比如

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

將b元素的分值由200修改成201,其他元素沒(méi)有變化,排名不變。

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

將b由200修改成301,排名發(fā)生變化

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

刪除原來(lái)的元素,再插入新的

刪除邏輯 zslDeleteNode

(1)找到待刪除的節(jié)點(diǎn),刪除節(jié)點(diǎn),更新前面節(jié)點(diǎn)的跨度和跳表最大層高

(2)更新新節(jié)點(diǎn)的各層的forward以及backward指針

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

新增邏輯 zslInsert

(1)首先,插入結(jié)點(diǎn)時(shí),都會(huì)隨機(jī)為新的節(jié)點(diǎn)分配一個(gè)小于64的層數(shù),并且層數(shù)越小概率越大,層數(shù)越高概率越小

(2)查找到小于待插入score的分?jǐn)?shù)值中最大的那個(gè)節(jié)點(diǎn),如果score相等,則通過(guò)value比較

(3)在找到的節(jié)點(diǎn)后面插入新的節(jié)點(diǎn)。同時(shí)更新前面節(jié)點(diǎn)的跨度和跳表最大層高

(4)更新新節(jié)點(diǎn)的各層的forward以及backward指針

插入節(jié)點(diǎn),最關(guān)鍵在于索引層的創(chuàng)建和指針的關(guān)聯(lián)關(guān)系

第一步:找插入的位置

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

第二步:創(chuàng)建索引層

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

在創(chuàng)建節(jié)點(diǎn)之前,先創(chuàng)建索引層,通過(guò)隨機(jī)算法生成

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

while循環(huán)的條件是隨機(jī)數(shù)random 小于 P=1/4 ,結(jié)束循環(huán)的條件是1-P

1、

level=1 第一層索引的生成概率即生成完第一層索引循環(huán)就結(jié)束的概率是1-P=3/4

2、

level=2 第二層索引的生成概率即生成完第二層索引循環(huán)就結(jié)束的概率是 P*(1-P)

  • • 第一次level=1 生成第一層索引的概率是1/4
  • • 第二次level=2 生成第二層索引的概率是3/4

3、

level=3 第三層索引的生成概率是 P^2 * (1-P)

  • • 第一次level=1 生成第一層索引的概率是1/4
  • • 第二次level=2 生成第二層索引的概率是1/4
  • • 第三次level=3 生成第三層索引的概率是3/4

綜上:層數(shù)越高,生成的概率越低,導(dǎo)致越高層的索引就越少,越低層的索引更多。

層數(shù)越高,出現(xiàn)的概率越低,節(jié)點(diǎn)越少,能夠一次性判別的元素更多。

創(chuàng)建好索引之后,每個(gè)索引的初始化參數(shù)都設(shè)置好,包括span的重新設(shè)置。

第三步: 創(chuàng)建節(jié)點(diǎn)

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

索引層作為參數(shù)傳遞進(jìn)入

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

節(jié)點(diǎn)創(chuàng)建好之后,再更新節(jié)點(diǎn)關(guān)系,包括頭節(jié)點(diǎn)指向它的關(guān)系、和兩邊節(jié)點(diǎn)之間的關(guān)系都需要重新創(chuàng)建、span的重新計(jì)算

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

再把對(duì)應(yīng)的統(tǒng)計(jì)數(shù)據(jù)++,因?yàn)樾略隽艘粋€(gè)元素

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

Redis高級(jí)數(shù)據(jù)類(lèi)型BitMap位圖

bit是計(jì)算機(jī)最小的存儲(chǔ)單元,取值為0和1。

bitmap提供了對(duì)bit位的設(shè)值、操作、統(tǒng)計(jì)的命令,通常被用來(lái)在極小的空間消耗下通過(guò)位運(yùn)算(AND/OR/XOR/NOT)來(lái)實(shí)現(xiàn)對(duì)狀態(tài)的操作,常見(jiàn)的使用場(chǎng)景:

  • • 海量用戶(hù)系統(tǒng)的日活/周活統(tǒng)計(jì)
  • • 連續(xù)登錄用戶(hù)統(tǒng)計(jì)
  • • 海量用戶(hù)會(huì)員判斷
  • • 判斷是否狀態(tài)的記錄,如是否簽到

二進(jìn)制數(shù)據(jù)在內(nèi)存中是連續(xù)的空間

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

隨機(jī)索引到要操作的bit位的效率非常高,時(shí)間復(fù)雜度是O(1)。

  • • bitmap可以判斷某一個(gè)bit位上是0還是1
  • • 對(duì)一組bit流上的數(shù)據(jù)統(tǒng)計(jì)所以為1的個(gè)數(shù)
  • • 對(duì)兩個(gè)bit流進(jìn)行位運(yùn)算(&與、|或、異或XO)

bitmap基本操作

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

對(duì)二進(jìn)制序列進(jìn)行具體的操作

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

key就是在內(nèi)存中的一個(gè)二進(jìn)制bit序列,每個(gè)序列都有一個(gè)索引,就是它的順序,其實(shí)就是offset偏移量。value值是0或1。

bitmap底層是string類(lèi)型實(shí)現(xiàn)的,bitmap本身就是一個(gè)string。

help @string

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 


Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

setbit返回的0是指老值是0

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

其他bit位都默認(rèn)是0

Redis壓縮列表、跳表、位圖的實(shí)現(xiàn)原理

 

統(tǒng)計(jì)key所對(duì)應(yīng)的二進(jìn)制序列中bit位為1的個(gè)數(shù)。

分享到:
標(biāo)簽:Redis
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定