redis的資料結構

littlexiaoshuishui發表於2020-08-17

Redis支援多個資料庫,並且每個資料庫的資料是隔離的不能共享,並且基於單機才有,如果是叢集就沒有資料庫的概念。

用select命令可以手動切換redis資料庫,預設從0開始

redis> select 0

typedef struct redisDb {  //一個redis庫的結構
 int id;         // 資料庫ID標識
 dict *dict;     // 鍵空間,存放著所有的鍵值對
 dict *expires;  // 過期雜湊表,儲存著鍵的過期時間
 dict *watched_keys; // 被watch命令監控的key和相應client
 long long avg_ttl;  // 資料庫內所有鍵的平均TTL(生存時間)
} redisDb;

鍵空間

字典格式(這個字典的底層資料結構就是hash表),鍵是字串物件, 值對應五種不同物件, string物件list物件hash物件集合物件有序集合對象中任意一種redis物件。

typedef struct redisObject{ 
     //型別
     unsigned type:4;
     //編碼
     unsigned encoding:4;
     //指向底層資料結構的指標,一共有6種底層資料結構
     void *ptr;
     //引用計數
     int refcount;
     //記錄最後一次被程式訪問的時間
     unsigned lru:22;
}robj

1.String物件

Redis 是用 C 語言寫的,但Redis的字串是自己構建了一種名為 簡單動態字串(simple dynamic string,SDS)的抽象型別。

struct sdshdr{
     //記錄buf陣列中已使用位元組的數量
     //等於 SDS 儲存字串的長度
     int len;
     //記錄 buf 陣列中未使用位元組的數量
     int free;
     //位元組陣列,用於儲存字串
     char buf[];
}

string物件的編碼型別有 int,raw或者embstr。 int 編碼是用來儲存整數值,raw編碼是用來儲存長字串,而embstr是用來儲存短字串。

2.List物件

底層資料結構是雙向連結串列或者 壓縮列表(ziplist), 當列表儲存元素個數小於512個且每個元素長度小於64位元組時為ziplist, 可以更改list-max-ziplist-value選項和 list-max-ziplist-entries 選項進行配置。 壓縮列表其實就是把資料放在連續的記憶體中。

3.Hash物件

底層資料結構是雜湊表

# hash表
typedef struct dictht{
     //雜湊表節點陣列陣列
     dictEntry **table;
     //雜湊表大小
     unsigned long size;
     //雜湊表大小掩碼,用於計算索引值
     //總是等於 size-1
     unsigned long sizemask;
     //該雜湊表已有節點的數量
     unsigned long used;

}dictht

# hash表節點
typedef struct dictEntry{
     //鍵
     void *key;
     //值
     union{
          void *val;
          uint64_tu64;
          int64_ts64;
     }v; 
     //指向下一個具有相同索引值的雜湊表節點,形成連結串列,鏈地址法解決雜湊衝突問題
     struct dictEntry *next;
}dictEntry

4.Set物件,集合物件

底層資料結構是整數集合(intset)或者hash表, 當集合物件中所有元素都是整數且所有元素數量不超過512時為intset型別,可通過set-max-intset-entries 進行配置。

typedef struct intset{
     //編碼方式
     uint32_t encoding;
     //集合包含的元素數量
     uint32_t length;
     //儲存元素的陣列
     int8_t contents[];

}intset;

5.zset物件,有序集合

底層資料結構為 跳躍表(skiplist),是一種有序資料結構,它通過在每個節點中維持多個指向其它節點的指標,從而達到快速訪問節點的目的。

Redis使用的記憶體回收演算法是引用計數演算法LRU演算法
1.引用計數演算法:對於建立的每一個物件都有一個與之關聯的計數器,這個計數器記錄著該物件被使用的次數,垃圾收集器在進行垃圾回收時,對掃描到的每一個物件判斷一下計數器是否等於0,若等於0,就會釋放該物件佔用的記憶體空間,同時將該物件引用的其他物件的計數器進行減一操作。
2.LRU演算法:LRU是Least Recently Used的縮寫,即最近最少使用,是一種常用的頁面置換演算法,選擇最近最久未使用的頁面予以淘汰。該演算法賦予每個頁面一個訪問欄位,用來記錄該頁面自上次被訪問以來所經歷的時間 t,當必須淘汰一個頁面時,選擇現有頁面中其 t 值最大的,即最近最少使用的頁面給予淘汰。LRU演算法最為經典的實現,就是HashMap+Double LinkedList,時間複雜度為O(1),但是如果按照HashMap和雙向連結串列實現,需要額外的儲存存放next和prev指標,犧牲比較大的儲存空間,顯然是不划算的。所以Redis中的LRU演算法,就是隨機取出若干個key,然後按照訪問時間排序後,淘汰掉最不經常使用的那個。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章