Redis五種數據類型詳解


參考鏈接

一、簡介

  Redis的五大數據類型也稱五大數據對象;前面介紹過6大數據結構,Redis並沒有直接使用這些結構來實現鍵值對數據庫,而是使用這些結構構建了一個對象系統redisObject;這個對象系統包含了五大數據對象,字符串對象(string)、列表對象(list)、哈希對象(hash)、集合(set)對象和有序集合對象(zset);而這五大對象的底層數據編碼可以用命令OBJECT ENCODING來進行查看。

//redisObject
typedef struct redisObject {
// 類型屬性存儲的是對象的類型,也就是我們說的 string、list、hash、set、zset中的一種,
  //可以使用命令 TYPE key 來查看。
unsigned type:4; // 編碼,記錄了隊形所使用的編碼,即這個對象底層使用哪種數據結構實現。 unsigned encoding:4; // 指向底層實現數據結構的指針 void *ptr; // ... } robj;

  redis是以鍵值對存儲數據的,所以對象又分為鍵對象和值對象,即存儲一個key-value鍵值對會創建兩個對象,鍵對象和值對象。鍵對象總是一個字符串對象,而值對象可以是五大對象中的任意一種

二、字符串對象(string)

字符串對象底層數據結構實現為簡單動態字符串(SDS)和直接存儲,但其編碼方式可以是int、raw或者embstr,區別在於內存結構的不同

(1)int編碼

字符串保存的是整數值,並且這個正式可以用long類型來表示,那么其就會直接保存在redisObject的ptr屬性里,並將編碼設置為int,如圖:

 

(2)raw編碼

 字符串保存的大於32字節的字符串值,則使用簡單動態字符串(SDS)結構,並將編碼設置為raw,此時內存結構與SDS結構一致,內存分配次數為兩次,創建redisObject對象和sdshdr結構,如圖:

(3)embstr編碼

 字符串保存的小於等於32字節的字符串值,使用的也是簡單的動態字符串(SDS結構),但是內存結構做了優化,用於保存頓消的字符串;內存分配也只需要一次就可完成,分配一塊連續的空間即可,如圖:

 

 字符串對象總結:

    • 在Redis中,存儲long、double類型的浮點數是先轉換為字符串再進行存儲的。
    • raw與embstr編碼效果是相同的,不同在於內存分配與釋放,raw兩次,embstr一次。
    • embstr內存塊連續,能更好的利用緩存在來的優勢
    • int編碼和embstr編碼如果做追加字符串等操作,滿足條件下會被轉換為raw編碼;embstr編碼的對象是只讀的,一旦修改會先轉碼到raw。

  應用場景

    1.訪問量統計:每次訪問博客和文章使用 INCR 命令進行遞增。

    2.一般做一些復雜的技術功能的緩存。

三、列表對象(list)

列表對象的編碼可以是ziplist和linkedlist之一。

(1) ziplist編碼

ziplist編碼的列表對象底層實現是壓縮列表,每個壓縮列表節點保存了一個列表元素。

(2)linkedlist編碼

linkedlist編碼底層采用雙端鏈表實現,每個雙端鏈表節點都保存了一個字符串對象,在每個字符串對象內保存了一個列表元素。

列表對象編碼轉換:

    • 列表對象使用ziplist編碼需要滿足兩個條件:一是所有字符串長度都小於64字節,二是元素數量小於512,不滿足任意一個都會使用linkedlist編碼
    • 兩個條件的數字可以在Redis的配置文件中修改,list-max-ziplist-value選項和list-max-ziplist-entries選項。
    • 圖中StringObject就是上一節講到的字符串對象,字符串對象是唯一個在五大對象中作為嵌套對象使用的。

  應用場景:

    做簡單的消息隊列的功能;最新消息排行等功能(比如朋友圈的時間線)。

四、哈希對象(hash)

哈希對象的編碼可以是ziplist和hashtable之一。

(1)ziplist編碼

ziplist編碼的哈希對象底層實現是壓縮列表,在ziplist編碼的哈希對象中,key-value鍵值對是以緊密相連的方式放入壓縮鏈表的,先把key放入表尾,再放入value;鍵值對總是向表尾添加。

(2)hashtable編碼

hashtable編碼的哈希對象底層實現是字典,哈希對象中的每個key-value對都使用一個字典鍵值對來保存。

字典鍵值對即是,字典的鍵和值都是字符串對象,字典的鍵保存key-value的key,字典的值保存key-value的value。

哈希對象編碼轉換:

    • 哈希對象使用ziplist編碼需要滿足兩個條件:一是所有鍵值對的鍵和值的字符串長度都小於64字節;二是鍵值對數量小於512個;不滿足任意一個都使用hashtable編碼。
    • 以上兩個條件可以在Reids配置文件中修改hash-max-ziplist-value選項和hash-max-ziplist-entries選項。

  應用場景:

    存儲、讀取、修改對象屬性,比如:用戶(姓名、性別、愛好),文章(標題、發布時間、作者、內容)

五、集合對象(set)

集合對象的編碼可以是intset和hashtable之一。

(1)intset編碼

intset編碼的集合對象底層實現是整數集合,所有元素都保存在整數集合中。

(2)hashtable編碼

hashtable編碼的集合對象底層實現是字典,字典的每個鍵都是一個字符串對象,保存一個集合元素,不同的是字典的值都是NULL;可以參考java中的hashset結構。

集合對象編碼轉換:

    • 集合對象使用intset編碼需要滿足兩個條件:一是所有元素都是整數值;二是元素個數小於等於512個;不滿足任意一條都將使用hashtable編碼。
    • 以上第二個條件可以在Redis配置文件中修改et-max-intset-entries選項。

  應用場景

  1、共同好友

  2、利用唯一性,統計訪問網站的所有獨立ip

 

  3、好友推薦時,根據tag求交集,大於某個閾值就可以推薦

 

六、有序集合對象(zset)

有序集合的編碼可以是ziplist和skiplist之一。

(1)ziplist編碼 

ziplist編碼的有序集合對象底層實現是壓縮列表,其結構與哈希對象類似,不同的是兩個緊密相連的壓縮列表節點,第一個保存元素的成員,第二個保存元素的分值,而且分值小的靠近表頭,大的靠近表尾。

(2)skiplist編碼

skiplist編碼的有序集合對象底層實現是跳躍表和字典兩種;

每個跳躍表節點都保存一個集合元素,並按分值從小到大排列;節點的object屬性保存了元素的成員,score屬性保存分值;

字典的每個鍵值對保存一個集合元素,字典的鍵保存元素的成員,字典的值保存分值。

為何skiplist編碼要同時使用跳躍表和字典實現?

    • 跳躍表優點是有序,但是查詢分值復雜度為O(logn);字典查詢分值復雜度為O(1) ,但是無序,所以結合連個結構的有點進行實現。
    • 雖然采用兩個結構但是集合的元素成員和分值是共享的,兩種結構通過指針指向同一地址,不會浪費內存。

有序集合編碼轉換:

  • 有序集合對象使用ziplist編碼需要滿足兩個條件:一是所有元素長度小於64字節;二是元素個數小於128個;不滿足任意一條件將使用skiplist編碼。
  • 以上兩個條件可以在Redis配置文件中修改zset-max-ziplist-entries選項和zset-max-ziplist-value選項。

  應用場景

    排行榜,取TopN操作。帶權重的消息隊列。

7、總結

在Redis的五大數據對象中,string對象是唯一個可以被其他四種數據對象作為內嵌對象的;

列表(list)、哈希(hash)、集合(set)、有序集合(zset)底層實現都用到了壓縮列表結構,並且使用壓縮列表結構的條件都是在元素個數比較少、字節長度較短的情況下;

四種數據對象使用壓縮列表的優點:

(1)節約內存,減少內存開銷,Redis是內存型數據庫,所以一定情況下減少內存開銷是非常有必要的。

(2)減少內存碎片,壓縮列表的內存塊是連續的,並分配內存的次數一次即可。

(3)壓縮列表的新增、刪除、查找操作的平均時間復雜度是O(N),在N再一定的范圍內,這個時間幾乎是可以忽略的,並且N的上限值是可以配置的。

(4)四種數據對象都有兩種編碼結構,靈活性增加。

 


免責聲明!

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



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