文章目錄
- 1 redis 存儲結構
-
- 1.1 redis 存儲結構大緻架構
- 1.3 部分的redis源碼展示說明
- 2 redis 存儲轉換
- 3 redis的底層資料結構描述
-
- 3.1 string
-
- 3.1.1 int
- 3.1.2 raw
- 3.1.3 embstr
- 3.2 list
-
- 3.2.1 quicklist 雙向連結清單
- 3.2.2 ziplist 壓縮清單
- 3.3 hash
-
- 3.3.1 dict(字典)
- 3.3.2 壓縮清單
- 3.4 set
-
- 3.4.1 insert 整形數組
- 3.4.2 dict 字典
- 3.5 zset
-
- 3.5.1 skiplist 跳表(後期會專門出一篇部落格介紹跳表結構)
- 3.5.2 ziplist
1 redis 存儲結構
1.1 redis 存儲結構大緻架構
1.3 部分的redis源碼展示說明
redis中資料庫的結構定義
16個資料庫由一個資料庫對象指針管理,在redis-server的時候,dbnum預設值是16
2 redis 存儲轉換
此圖已經概括了redis中五種資料類型的全部底層資料結構的劃分和存儲轉換
3 redis的底層資料結構描述
3.1 string
3.1.1 int
字元串長度小于等于20 ,并且能夠轉成整數。
string 資料類型有一個位圖的功能,但是存儲位圖資料用的是raw 或者是embstr,原因是因為 raw 和embstr 是sds動态字元串,能夠節約記憶體。當然int 是4個位元組也是可以用作32位的位圖,但是其大小固定比較單一。
3.1.2 raw
raw 也是string的底層資料結構,當字元串的長度大于44時,底層使用raw結構,需要配置設定兩次記憶體空間(分别為 Redis Object 和 SDS 配置設定空間)。
3.1.3 embstr
當字元串長度小于44時,底層使用
隻配置設定一次記憶體空間(因為 Redis Object 和 SDS 是連續的)。
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
int refcount;
void *ptr;
} robj;
struct __attribute__ ((__packed__)) hisdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
當位元組數小于44時,配置設定的大小一直都是64個位元組
sizeof(struct redisObject) = 16;
sizeof(hisdshdr8) = 3;
還有一個\0 一個位元組
64 - sizeof(struct redisObject) - sizeof(hisdshdr8) -sizeof(“\0”) =44;
是以44 作為raw和embstr的分界點。
3.2 list
3.2.1 quicklist 雙向連結清單
這個不做多個解釋
3.2.2 ziplist 壓縮清單
這個在list隻是間接的使用
ziplist壓縮清單詳情
ziplist存儲是連續的記憶體空間,可以做壓縮。當設計計算時,ziplist明顯會比雙向連結清單的指針檢索慢,是以ziplist是犧牲時間換取空間的結構。list的底層采取的是壓縮清單加雙向連結清單的存儲結構,quicklist 為了存儲更多的資料,會對每個 quicklistNode 節點進行壓縮,這樣就可以有效的存儲更多的消息隊列或者文章的資料了。
當實作為雙向連結清單時,節點會有pre和next指針,每一個指針占8個位元組。而ziplist的entry中,用previous_entry_length儲存上一個entry的長度,當上一個entry長度小于等于263位元組時,previous_entry_length隻占一個位元組;大于263位元組時,previous_entry_length占5個位元組,并且ziplist存儲是連續的記憶體空間,可以做壓縮。當設計計算時,ziplist明顯會比雙向連結清單的指針檢索慢,是以ziplist是犧牲時間換取空間的結構
清單的典型使用場景有以下兩個:
消息隊列:清單類型可以使用 rpush 實作先進先出的功能,同時又可以使用 lpop 輕松的彈出(查詢并删除)第一個元素,是以清單類型可以用來實作消息隊列;
文章清單:對于部落格站點來說,當使用者和文章都越來越多時,為了加快程式的響應速度,我們可以把使用者自己的文章存入到 List 中,因為 List 是有序的結構,是以這樣又可以完美的實作分頁功能,進而加速了程式的響應速度。
zlbytes:壓縮清單位元組長度,占 4 位元組;
zltail:壓縮清單尾元素相對于起始元素位址的偏移量,占 4 位元組;
zllen:壓縮清單的元素個數;
entryX:壓縮清單存儲的所有元素,可以是位元組數組或者是整數;
zlend:壓縮清單的結尾,占 1 位元組。
3.3 hash
3.3.1 dict(字典)
typedef struct dict {
dictEntry **table;
dictType *type;
unsigned long size;
unsigned long sizemask;
unsigned long used;
void *privdata;
} dict;
table :hash指針數組
type: 是以dictType通過函數指針的方式,将不同資料類型的操作都封裝起來。從面相對象的角度來看,可以把dictType當成dict中各種資料類型相關操作的interface,各個資料類型隻需要實作其對應的資料操作就行
size:hash 數組的大小 通常是2的n次方
sizemask:size-1 一般用作&
used:已經存儲的資料個數
其中dictEntry就是對dict中每對key-value的封裝,除了具體的key-value,其還包含一些其他資訊,具體如下:
typedef struct dictEntry {
void *key;
union { // dictEntry在不同用途時存儲不同的資料
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; // hash沖突時開鍊,單連結清單的next指針
} dictEntry;
3.3.2 壓縮清單
上面已經講過。當hash的資料結構中位元組數量大于等于512時且字元串長度小于等于64,hash的資料類型才會用壓縮清單這個資料結構。
3.4 set
3.4.1 insert 整形數組
元素都為整數且節點數量小于等于512時用整形數組
3.4.2 dict 字典
元素有一個不為整數 或者節點數量大于512 用dict
3.5 zset
3.5.1 skiplist 跳表(後期會專門出一篇部落格介紹跳表結構)
節點數量大于128 或者字元串長度大于一64
3.5.2 ziplist
節點數量小于128且字元串長度小于64