溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Redis中快速表、壓縮表和雙向鏈表的區(qū)別有哪些

發(fā)布時間:2021-04-07 09:38:40 來源:億速云 閱讀:258 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹Redis中快速表、壓縮表和雙向鏈表的區(qū)別有哪些,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

前言

最近在看《Redis的設(shè)計(jì)與實(shí)現(xiàn)》這本書,寫的真的是太好了,一下子就看入迷了,謝謝作者。不過在學(xué)習(xí)的時候發(fā)現(xiàn)一個問題,我服務(wù)器上安裝的是Redis5.0.9版本的,而作者介紹的是Redis3.0版本的,在第一部分將數(shù)據(jù)結(jié)構(gòu)與對象章節(jié)的時候,出現(xiàn)了一些差別,就是在redis對外暴露的list結(jié)構(gòu)底層使用的數(shù)據(jù)結(jié)構(gòu)問題。由于書上沒有記錄,所以就在網(wǎng)上查閱了些資料學(xué)習(xí)了一下, 自己再做個總結(jié),當(dāng)做自己的筆記。

差別

出現(xiàn)的差別就是,在redis3.2版本之前,它使用的是ziplist和linkedlist編碼作為列表鍵的底層實(shí)現(xiàn),在它之后,就采用了一個叫做quicklist的數(shù)據(jù)結(jié)構(gòu)來作其底層實(shí)現(xiàn)。
先來介紹下redis3.2之前的版本的知識點(diǎn):
在使用ziplist和linkedlist作為列表鍵底層實(shí)現(xiàn)的時候,他們之間會有一個選擇標(biāo)準(zhǔn):
選擇ziplist的時候:

  • 列表對象保存的所有字符串元素的長度都小于64字節(jié);

  • 列表對象保存的元素量小于512個

上面的是選擇ziplist作為底層實(shí)現(xiàn)所必須滿足的條件,如果沒滿足的話就選用linkedlist作為其底層實(shí)現(xiàn)。

127.0.0.1:6379> rpush blah "hello" "world" "again"
3
127.0.0.1:6379> object encoding blah
ziplist
127.0.0.1:6379> rpush blah "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
4
127.0.0.1:6379> object encoding blah
linkedlist

再來介紹下redis3.2之后的版本:

這個涉及到quicklist這個數(shù)據(jù)結(jié)構(gòu)了,書上沒有記錄,所以我查了下資料,也總結(jié)到自己的博客當(dāng)中。

我安裝的時候redis5.0.9版本的,所以上面的幾個指令執(zhí)行的結(jié)果會有所不同

127.0.0.1:6379> rpush blah "hello" "world" "again"
3
127.0.0.1:6379> object encoding blah
quicklist
127.0.0.1:6379> rpush blah "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
4
127.0.0.1:6379> object encoding blah
quicklist

quicklist數(shù)據(jù)結(jié)構(gòu)的介紹

ziplist和linkedlist就不介紹了,書本上有,我們來看看quicklist。
quicklist其實(shí)現(xiàn)也是依賴于ziplist和linkedlist來實(shí)現(xiàn)的,它是兩個結(jié)構(gòu)的結(jié)合。它將ziplist來進(jìn)行分段存儲,也就是分成一個個的quicklistNode節(jié)點(diǎn)來進(jìn)行存儲。每個quicklistNode指向一個ziplist,然后quicklistNode之間是通過雙向指針來進(jìn)行連接的。我們來看下大致的結(jié)構(gòu):

Redis中快速表、壓縮表和雙向鏈表的區(qū)別有哪些

看到這個結(jié)構(gòu)可能會有些疑問:

  • 這個結(jié)構(gòu)啥意思啊,為什么有的節(jié)點(diǎn)是ziplist,有的是quicklistZF?

  • 為什么quicklist頭部和尾部各有兩個節(jié)點(diǎn)是ziplist,剩下中間的是quicklistZF?

  • 為什么每個quicklistNode節(jié)點(diǎn)的ziplist內(nèi)部的數(shù)據(jù)個數(shù)不一致呢?

為什么要把底層數(shù)據(jù)結(jié)構(gòu)優(yōu)化成quicklist?

在解決上面的問題之前我們先來解決一個首要的問題,也就是redis為什么要把列表鍵的底層數(shù)據(jù)結(jié)構(gòu)優(yōu)化成quicklist?
其實(shí)可以從兩個方面來考慮這個原因:

  • ziplist這個結(jié)構(gòu),它內(nèi)部的數(shù)據(jù)存儲是一段連續(xù)的空間,這樣的話,就要求很大一塊內(nèi)存空間。就比如說,我們想存儲很多的數(shù)據(jù),但是內(nèi)存中并沒有符合要求的連續(xù)的存儲空間,而是存在很多不連續(xù)的小空間(加起來可以符合要求)。

  • 再來說說linkedlist這個結(jié)構(gòu),它數(shù)據(jù)存儲不要求連續(xù),就可以避免上面的弊端,不過這樣以來,每個節(jié)點(diǎn)都分配一個一塊內(nèi)存,那就有可能造成大量的內(nèi)存碎片。

綜上兩個考慮,redis在3.2版本以后就對此情況進(jìn)行了優(yōu)化,就出來了quicklist這個數(shù)據(jù)結(jié)構(gòu),quicklist其實(shí)就是一個分段的ziplist,為什么這么說呢?其實(shí)quicklist存儲數(shù)據(jù)基本單位是quicklistNode,每個quicklistNode的內(nèi)容區(qū)就是以ziplist數(shù)據(jù)結(jié)構(gòu)存儲的。也就是上面圖示展示的。

為什么每個quicklistNode節(jié)點(diǎn)的ziplist內(nèi)部的數(shù)據(jù)個數(shù)不一致呢?

現(xiàn)在轉(zhuǎn)化到quicklist了,但是還需要考慮,quicklistNode里的ziplist里的內(nèi)容處理呢?一個ziplist我需要存儲多少個數(shù)據(jù)呀?也跟上面兩個點(diǎn)考慮的一樣

  • 如果ziplist里的內(nèi)容分配的越少的話,也就是往linkedlist方向發(fā)展了,就可能會產(chǎn)生很多的內(nèi)存碎片

  • 但要如果ziplist里的內(nèi)容分配的越多的話,也會出現(xiàn)問題,就是需要很大一塊連續(xù)的內(nèi)存空間。

redis設(shè)計(jì)者已經(jīng)給我們想好了,它的配置文件里有這樣一個參數(shù):list-max-ziplist-size

Redis中快速表、壓縮表和雙向鏈表的區(qū)別有哪些

看到這個配置,我們來解釋下這個參數(shù),它既可以設(shè)置正數(shù),也可以設(shè)置負(fù)數(shù)

當(dāng)它取正數(shù)的時候,表示按照數(shù)據(jù)項(xiàng)個數(shù)來限定quicklist節(jié)點(diǎn)上的ziplist長度,比如我們設(shè)置為4,就說明每個ziplist的數(shù)據(jù)項(xiàng)最多不能超過5個

當(dāng)它取負(fù)數(shù)的時候,表示按照占用字節(jié)來限定quicklsit節(jié)點(diǎn)上的ziplist長度,這時,它只能取-1到-5這五個值,每個值的含義如下:
1)、-5:每個quicklist節(jié)點(diǎn)上的ziplist大小不能超過64kb。(1kb == 1024 byte)
2)、-4:每個quicklsit節(jié)點(diǎn)上的ziplist大小不能超過32kb。
3)、-3:每個quicklsit節(jié)點(diǎn)上的ziplist大小不能超過16kb。
4)、-2:每個quicklsit節(jié)點(diǎn)上的ziplist大小不能超過8kb。
5)、-1:每個quicklsit節(jié)點(diǎn)上的ziplist大小不能超過4kb。

所以就會出現(xiàn)問題所說的,ziplist內(nèi)存存儲數(shù)據(jù)個數(shù)不一致的問題,我們也可以自己手動設(shè)置參數(shù)值。

這個結(jié)構(gòu)啥意思啊,為什么有的節(jié)點(diǎn)是ziplist,有的是quicklistZF?

這里又出現(xiàn)了一個配置參數(shù):list-compress-depth
其實(shí),當(dāng)鏈表很長的時候,最頻繁訪問的就是兩端的數(shù)據(jù),中間被訪問的頻率比較低,所以我們可以將中間部分節(jié)點(diǎn)進(jìn)行壓縮,從而能夠進(jìn)一步節(jié)省空間。上面說的list-compress-depth就是來設(shè)置這個的。
這個參數(shù)的取值含義如下:

0:是個特殊值,表示都不壓縮。這是redis的默認(rèn)值

1:表示quicklist兩端各有一個節(jié)點(diǎn)不被壓縮,中間節(jié)點(diǎn)進(jìn)行壓縮

2:表示quicklist兩端各有兩個節(jié)點(diǎn)不被壓縮,中間節(jié)點(diǎn)進(jìn)行壓縮

3:表示quicklist兩端各有三個節(jié)點(diǎn)不被壓縮,中間節(jié)點(diǎn)進(jìn)行壓縮…

以此類推

也就是會出現(xiàn)問題所說的,兩端節(jié)點(diǎn)為ziplist,中間節(jié)點(diǎn)為quicklistZF

Redis中快速表、壓縮表和雙向鏈表的區(qū)別有哪些

我們看看上面的redis配置文件的默認(rèn)配置。

簡單了解源碼結(jié)構(gòu)

我通過上面這個圖來簡單說一下quicklist的存儲方式,它底層有兩個結(jié)構(gòu),一個quicklist,一個就是quicklistNode。下面是我找的源碼,可以簡單看下。

typedef struct quicklistNode {
  struct quicklistNode *prev;
  struct quicklistNode *next;
  unsigned char *zl;
  unsigned int sz;       /* ziplist size in bytes */
  unsigned int count : 16;   /* count of items in ziplist */
  unsigned int encoding : 2;  /* RAW==1 or LZF==2 */
  unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
  unsigned int recompress : 1; /* was this node previous compressed? */
  unsigned int attempted_compress : 1; /* node can't compress; too small */
  unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;

typedef struct quicklistLZF {
  unsigned int sz; /* LZF size in bytes*/
  char compressed[];
} quicklistLZF;

typedef struct quicklist {
  quicklistNode *head;
  quicklistNode *tail;
  unsigned long count; /* total count of all entries in all ziplists */
  unsigned int len; /* number of quicklistNodes */
  int fill : 16; /* fill factor for individual nodes */
  unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;

quicklist的主要作用就是來指向節(jié)點(diǎn)的頭和尾

總的來說quicklist就是對ziplist和linkedlist兩者優(yōu)點(diǎn)的結(jié)合,來進(jìn)一步優(yōu)化redis的列表鍵底層存儲。

以上是“Redis中快速表、壓縮表和雙向鏈表的區(qū)別有哪些”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI