redis-02 五種類型底層數據結構


redis有五種基本數據結構:字符串、hash、set、zset、list。但是你知道構成這五種結構的底層數據結構是怎樣的嗎? 今天我們來花費五分鍾的時間了解一下。

1、string

  這里不得不提動態字符串 SDS,即 ”simple dynamic string" 的縮寫。redis 中所有場景中出現的字符串,基本都是由SDS來實現的。

  

   free:還剩多少空間; len:字符串長度; buf:存放的字符數組

 空間預分配

  為減少修改字符串帶來的內存重分配次數,SDS 采用了 “一次管夠” 的策略:

  • 若修改之后 SDS 長度 < 1MB,則多分配現有 len長度 的空間
  • 若修改之后 SDS 長度 >= 1MB,則擴充除了滿足修改之后的長度外,額外多 1MB 空間

 惰性空間釋放

  為避免縮短字符串時候的內存重分配操作,SDS 在數據減少時,並不立刻釋放空間。

  

  其中:embstr 和 raw 都是由 SDS 動態字符串構成的。

  唯一區別是:raw 是分配內存的時候,redis object 和 SDS 各分配一塊內存,而 embstr 是 redis object 和 raw 在一塊兒內存中。

2、list(列表)

  

  ziplist 稱為 壓縮列表;linkedlist 稱為 雙向列表;

  在版本3.2之前,Redis 列表list使用兩種數據結構作為底層實現:

  • 壓縮列表 ziplist
  • 雙向鏈表linkedlist
    因為雙向鏈表占用的內存比壓縮列表要多,所以當創建新的列表鍵時,列表會優先考慮使用壓縮列表,並且在有需要的時候,才從壓縮列表實現轉換到雙向鏈表實現。

  雙向鏈表linkedlist

    linkedlist是標准的雙向鏈表,Node節點包含prev和next指針,可以進行雙向遍歷;

    還保存了 head 和 tail 兩個指針,因此,對鏈表的表頭和表尾進行插入的復雜度都為 (1) —— 這是高效實現 LPUSH 、 RPOP、 RPOPLPUSH 等命令的關鍵。

  

  壓縮列表 ziplist

    它是為 Redis 節約內存而開發的。

    ziplist 是由一系列特殊編碼的內存塊構成的列表(像內存連續的數組,但每個元素長度不同), 一個 ziplist 可以包含多個節點(entry)。

    ziplist 將表中每一項存放在前后連續的地址空間內,每一項因占用的空間不同,而采用變長編碼。由於內存是連續分配的,所以遍歷速度很快。

    

 

   redis3.2+ 之 list 的新實現 quickList

    Redis 中的列表 list,在版本 3.2 之前,列表底層的編碼是ziplist和linkedlist實現的,但是在版本3.2之后,重新引入 quicklist,列表的底層都由 quicklist 實現。

    在版本3.2之前,當列表對象中元素的長度比較小或者數量比較少的時候,采用 ziplist 來存儲,當列表對象中元素的長度比較大或者數量比較多的時候,則會轉而使用雙向列表 linkedlist 來存儲。

  這兩種存儲方式的優缺點

  • 雙向鏈表linkedlist便於在表的兩端進行push和pop操作,在插入節點上復雜度很低,但是它的內存開銷比較大。首先,它在每個節點上除了要保存數據之外,還要額外保存兩個指針;其次,雙向鏈表的各個節點是單獨的內存塊,地址不連續,節點多了容易產生內存碎片。
  • ziplist存儲在一段連續的內存上,所以存儲效率很高。但是,它不利於修改操作,插入和刪除操作需要頻繁的申請和釋放內存。特別是當ziplist長度很長的時候,一次realloc可能會導致大批量的數據拷貝。 

  quickList

    可以認為quickList,是ziplist和linkedlist二者的結合;quickList將二者的優點結合起來。

    quickList是一個ziplist組成的雙向鏈表。每個節點使用ziplist來保存數據。本質上來說,quicklist里面保存着一個一個小的 redisziplist。結構如下:

        

3、hash

  

   哈希表解決沖突的方法一般有兩種,一種是:開放尋址法,一種是:拉鏈法。redis的哈希表解決沖突使用的是 拉鏈法

   整體結構如下圖:

   

    其中有兩個關鍵的屬性:ht rehashidx。 ht是一個數組,有且只有倆元素 ht[0] 和 ht[1];其中,ht[0] 存放的是 redis 中使用的哈希表,而ht[1] 和 rehashidx 和 哈希表的 rehash有關。

   且 ht[1] 后續的收縮和擴容要與 ht[0] 進行交換。且對應哈希表的擴容都是 2 倍增長的。

 4、set

  

   intset 即 整數集合是集合鍵的底層實現方式之一。類似於 ziplist 存儲結構

 

 5、zset

  

   跳表這種數據結構長這樣:

   

   redis中把跳表抽象成如下所示:

   

  看這個圖,左邊“統籌”,右邊實現。 統籌部分有以下幾點說明:

  • header: 跳表表頭
  • tail:跳表表尾
  • level:層數最大的那個節點的層數
  • length:跳表的長度

  實現部分有以下幾點說明:

  • 表頭:是鏈表的哨兵節點,不記錄主體數據。
  • 是個雙向鏈表
  • 分值是有順序的
  • o1、o2、o3 是節點所保存的成員,是一個指針,可以指向一個SDS值。
  • 層級高度最高是32。沒每次創建一個新的節點的時候,程序都會隨機生成一個介於1和32之間的值作為level數組的大小,這個大小就是“高度”

可參考微博:https://juejin.im/post/5de3e841f265da05d03826ca#heading-26


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM