細談Redis五大數據類型

文章原創於公衆號:程序猿周先森。本平臺不定時更新,喜歡個人文章,歡迎關注個人微信公衆號。
file程序員

上一篇文章有提到,Redis中使用最頻繁的有5種數據類型:String、List、Hash、Set、SortSet。上一篇文章只是單純介紹了下這5種數據類型使用到的指令以及經常使用場景,本篇文章會談談5種數據類型的底層數據結構以及各自經常使用的操做命令來分別進行解析。Redis做爲目前最流行的Key-Value型內存數據庫,不只數據庫操做在內存中進行,而且可按期的將數據持久化到磁盤中,因此性能相對普通數據庫高不少,而在Redis中,每一個Value實際上都是以一個redisObject結構來表示:
typedef struct redisObject{redis

unsigned type:4;
    unsigned encoding:4;
    void *ptr;
    int refCount;
    unsigned lru:

}
咱們能夠看看這幾個參數分別的含義:算法

  • type:對象的數據類型,通常狀況就是5大數據類型。
  • encode:redisObject對象底層編碼實現,主要編碼類型有簡單動態字符串,鏈表,字典,跳躍表,整數集合及壓縮列表。
  • *ptr:指向底層實現數據結構的指針。
  • refCount:計數器,當引用計數值爲0將會釋放對象。
  • lru:最後一次訪問本對象的時間。

String數據類型數據庫

String 數據結構是簡單的 Key-Value 類型,是Redis中最經常使用的一種數據類型,Value 能夠是string或者數字。String數據類型實際上能夠存儲字符串、整數、浮點數三種不一樣類型的值,Redis是如何作到自動識別字符串、整數、浮點數三種不一樣類型的值。Redis是使用C實現的,可是並未使用C中的字符串,實際上Redis本身實現了一個結構體SDS來替代String類型:
struct sdshdr{數組

//記錄buf數組中已使用字節的長度
    int len;
    //記錄buf數組中剩餘空間的長度
    int free;
    //字節數組,用於存儲字符串
    char buf[];

};微信

咱們能夠看到free參數是用來判斷剩餘可以使用空間的長度,len表示字符串的長度,buf存儲字符串的每個字符以及結尾的'0'。爲何Redis要本身實現SDS結構體呢?由於SDS結構體有幾個優勢:session

  • 因爲len保存了當前字符串的實際長度,因此獲取長度時間複雜度爲O(1)。
  • SDS在拼接以前會對當前字符串的空間進行自動調整和擴展,防止當前字符串數據溢出。
  • 減小內存分配次數,SDS拼接字符串發生時,若是此時的字符串長度len小於1M,則SDS會分配和len大小相同的未使用空間給free,若是此時的字符串長度len大於1M,則SDS會分配和1M的未使用空間給free,當字符串縮短時,縮短的空間會疊加到free中,用於後續的拼接使用。

String數據類型經常使用命令:數據結構

  • 經常使用命令:set、get、decr、incr、mget 等。

String數據類型適用場景:分佈式

  • 分佈式鎖
  • 分佈式session:將分佈式應用session存儲到Redis中
  • 商品秒殺
  • 常規計數:博客數,閱讀數

List數據類型函數

List數據結構是用來存儲多個有序的字符串,List中的每一個字符串成爲元素,List提供了節點重排和節點順序訪問的能力,在Redis中,List能夠在兩端push和pop元素,還能夠獲取指定範圍的元素列表,獲取指定索引下標的元素等,List數據結構主要有zipList(壓縮鏈表)和LinkedList(雙向鏈表)兩種實現方式。首先咱們能夠先看看LinkedList的結構:

type struct list{

//表頭節點
        listNode *head;
        //表尾節點
        listNode *tail;
        //包含的節點總數
        unsigned long len;

};

能夠看到每一個LinkedList中都會包含一個表頭節點head和一個表尾結點tail,在LinkedList中每一個節點都會有一個prev指向前一個元素,同時還有一個next指向後一個元素,每一個節點的value就是節點的值。從而實現雙向鏈表,理解起來實際上和C中的雙向鏈表有很大程度的類似性。而另外一種實現方式zipList是基於連續內存實現,有點相似於數組方式,可是和數組有點不一致的是zipList的每個entry的大小可能不一致,須要特殊方法去控制解決,可是在執行push,pop操做時會有數據的遷移,時間複雜度爲O(n), 因此通常只有在元素較少時纔會使用zipList,咱們能夠看看zipList的結構:

type struct ziplist{

//整個壓縮列表的字節數
        uint32_t zlbytes;
        //記錄壓縮列表尾節點到頭結點的字節數,直接能夠求節點的地址
        uint32_t zltail_offset;
        //記錄了節點數,有多種類型,默認以下
        uint16_t zllength;
        //節點
        List entryX;

}

zipList中每一個節點都會有如下幾個參數信息:

  • previous_entry_length:記錄前一個節點的字節長度
  • content:節點所存儲的內容,能夠是一個字節數組或者整數
  • encoding:記錄content屬性中所保存的數據類型以及長度

* List數據類型適用場景

在渲染文章列表時可使用List數據類型,通常狀況下每一個用戶都會有本身發佈的文章列表,若是須要展現文章列表,就可使用List數據類型,不但能夠有序並且能夠按照索引範圍去查詢文章列表。

Set數據類型

Set數據類型和List數據類型有點相似,也能夠用來保存多個元素,但最大的一點區別在於Set數據類型不容許出現重複的元素,而且Set中的元素是無序的,因此沒辦法和List同樣經過索引下標獲取元素,可是Set類型支持多個Set集合取交集、並集、差集,因此合理使用Set數據類型,能夠在實際項目開發中解決不少問題。Set數據類型有兩種數據結構:IntSet和HashTable。首先咱們來看看IntSet的結構:

typedef struct intset {

// 編碼方式
    uint32_t enconding;
    // 集合包含的元素數量
    uint32_t length;
    // 保存元素的數組 
    int8_t contents[];

} intset;

當Set集合中全部元素都爲整型時,Redis纔會使用IntSet數據結構。有一點須要格外注意的是:IntSet數據結構是有序的。由於爲了減輕性能的消耗,Redis在Set集合元素都爲整型時,會使用一種基於動態數組的結構體,同時在push元素的時候控制元素的大小順序,這樣就可使用二分查找算法來對元素進行push及pop操做,這樣時間複雜度僅爲O(logN)。在Set集合中元素存在非整型數據時,Redis這時會自動採用HashTable數據結構來存放數據,在HashTable中,存放的只有key值而沒有value值,因此說在HashTable中,鍵值永遠爲null。咱們能夠看下HashTable的結構:

typedef struct dict{

//類型特定函數
    dictType *type;
    //哈希表 兩個,一個用於實時存儲,一個用於rehash
    dictht ht[2];
    //rehash索引 數據遷移時使用
    unsigned rehashidx;

}

Set數據類型使用場景:

  • 記錄惟一值:好比登陸ip,身份證號
  • 添加標籤:能夠經過標籤的交併集計算用戶喜愛程度等數據。

Hash數據類型
在Redis中哈希類型是指鍵自己又是一種鍵值對結構,也就是咱們所說的對象,因此Hash數據類型用來存儲對象是最合適的數據類型。Hash數據類型的編碼能夠是zipList或HashTable。當哈希對象保存的全部鍵值對長度小於64字節而且元素數量少於512時使用zipList,不然使用HashTable。zipList與剛纔List數據類型中講到的zipList實際上基本一致,惟一區別在於Hash存儲entry數量成對增長,因此長度必定爲2的整數倍。固然,使用zipList剛纔已經說過push和pop時間複雜度爲O(n),因此只能在數據量少的狀況下才容許使用。而HashTable其實有點相似於Java中的HashTable,HashTTable主要依賴於三個結構:dict、dictht、entry。三個結構的關係能夠表示爲以下這幅圖:
file

Hash數據類型適用場景:

  • 存儲對象數據。
  • 結合Json描述對象集合。

SortSet數據類型

有序集合是在Set集合的基礎上,保留了Set集合中不能存在重複元素的特性,可是不一樣的是,SortSet集合中元素是能夠排序的,SortSet排序和List排序均可以使用索引下標做爲排序依據,因此說SortSet實現了數據有序且鍵值對惟一的集合,SortSet的數據結構有兩種:zipList和skipList + HashTable,zipList都不用多少了,是用於數據量較少的狀況,默認排序爲元素從小到大。而採用skipList + HashTable的數據結構,skipList會在保證集合有序的狀況下優化範圍查找的時間複雜性,而HashTable剛纔已經提到過它能夠優化push和pop元素時的時間複雜性。skipList基於有序鏈表,能夠建立多層索引,實現以空間複雜度來換取時間複雜度的作法,最終實現時間複雜度爲O(logN)的元素查詢過程,當須要push或者pop元素時,則使用HashTable實現時間複雜度僅爲O(1).

SortSet數據類型適用場景

  • 積分排行榜:根據積分排序從小到大
  • 獲取某個範圍的數據:考試80-100分的數據

歡迎關注公衆號:程序員周先森
file

相關文章
相關標籤/搜索