redis的數據庫對象有五種,分別是字符串對象(key-value),列表對象(list),哈希對象(hash),集合對象(set),有序集合(sort set)。html
這些數據對象來自於底層數據類型的實現,這些數據類型分別爲簡單動態字符串,鏈表,字典,跳躍表,整數集合,壓縮列表,對象。java
如下是redisObject和底層數據結構的關係redis
redis的鍵(KEY)由簡單動態字符串實現,字符串對象(key-value)也是由簡單動態字符串實現的。而簡單動態字符串是由C實現的,它的結構以下算法
struct sdshr{ //記錄buf中已經使用的字節數量 unsigned int len; //記錄buf中未使用的字節數量 unsigned int free; //字節數組,存儲字符串 char buf[]; }
在redis3.2版本後,SDS的結構發生了變化,有多種數據結構,根據狀況使用數據庫
//字符串長度小於32 Byte struct __attribute__ ((__packed__)) sdshdr5 { unsigned char flags; /* 記錄當前字節數組的屬性,究竟是哪一個SDS結構 */ char buf[];/* 保存字符串真正的值 */ }; //字符串長度小於256 Byte,大於32 Byte struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* 記錄當前數組的長度 */ uint8_t alloc; /* 記錄了當前字節數組總共分配的內存大小 */ unsigned char flags; /* 記錄當前字節數組的屬性,究竟是哪一個SDS結構 */ char buf[];/* 保存字符串真正的值 */ }; //字符串長度小於65536 Byte (64KB),大於256 Byte struct __attribute__ ((__packed__)) sdshdr16 { uint16_t len; /* 記錄當前數組的長度 */ uint16_t alloc; /* 記錄了當前字節數組總共分配的內存大小 */ unsigned char flags; /* 記錄當前字節數組的屬性,究竟是哪一個SDS結構 */ char buf[];/* 保存字符串真正的值 */ }; //字符串長度小於4294967296 Byte (4GB),大於65536 Byte struct __attribute__ ((__packed__)) sdshdr32 { uint32_t len; /* 記錄當前數組的長度 */ uint32_t alloc; /* 記錄了當前字節數組總共分配的內存大小 */ unsigned char flags; /* 記錄當前字節數組的屬性,究竟是哪一個SDS結構 */ char buf[];/* 保存字符串真正的值 */ }; //字符串長度大於4294967296 Byte (4GB) struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; /* 記錄當前數組的長度 */ uint64_t alloc; /* 記錄了當前字節數組總共分配的內存大小 */ unsigned char flags; /* 記錄當前字節數組的屬性,究竟是哪一個SDS結構 */ char buf[];/* 保存字符串真正的值 */ };
鏈表是redis對象列表(list)的一種實現。當列表中元素數量比較多,或元素中字符串數量比較大時,就會使用鏈表結構。鏈表方便順序查詢,同時也方便插入和刪除。數組
鏈表由listNode和list組成,其結構以下數據結構
typedef struct listNode{ struct listNode *prev;//前一個節點 struct listNode * next;//後一個節點 void * value;//節點值 }
typedef struct list{ //頭節點 listNode * head; //尾節點 listNode * tail; //長度 unsigned long len; //複製函數 void *(*dup) (void *ptr); //釋放函數 void (*free) (void *ptr); //對比函數 int (*match)(void *ptr, void *key); }
結構圖以下:函數
字典能夠用來保存鍵值對,字典的結構和java的hashMap結構類似,採用hash算法保存key。字典由字典,哈希表,哈希表節點實現。其結構以下:ui
/* * 哈希表節點 */ typedef struct dictEntry { // 鍵 void *key; // 值,能夠是三種形式:指針,8字節字符,8字節整數 union { void *val; uint64_t u64; int64_t s64; } v; //指向下個哈希表節點,造成鏈表(以下hash衝突,會造成鏈表) struct dictEntry *next; }
/* * 哈希表 */ typedef struct dictht { // 哈希表數組 // 裏面存儲dictEntry dictEntry **table; // 哈希表大小 unsigned long size; // 哈希表大小掩碼,用於計算索引值 // 老是等於 size - 1,index = hash & sizemask // 這樣全部計算得出的hash值均可以對應於table的索引 unsigned long sizemask; // 該哈希表已有節點的數量 unsigned long used; }
/* * 字典 */ typedef struct dict { // 類型特定函數,type和privdata是爲了多態字典所設置的 dictType *type; // 私有數據 void *privdata; // 哈希表,每一個字典包含兩個table // ht[0]是平時使用的哈希表,ht[1]是rehash時使用的哈希表 dictht ht[2]; // rehash 索引標識 // 當 rehash 不在進行時,值爲 -1 int rehashidx; }
字典結構圖以下:編碼
跳躍表是一種有序的數據結構,它經過score進行排序。跳躍表有層級的概念,每一個跳躍表節點有多層,每一個層級會按順序指向以後若干節點的對應層級。這是爲了便於查詢,經過層級查詢節點,會比按score順序依次查詢要快不少。節點和鏈表的結構以下:
typedef struct zskiplistNode{ //層 struct zskiplistLevel{ //前進指針 struct zskiplistNode *forward; //跨度 unsigned int span; } level[]; //後退指針 struct zskiplistNode *backward; //分值 double score; //成員對象 robj *obj; }
typedef struct zskiplist { //表頭節點和表尾節點 struct zskiplistNode *header,*tail; //表中節點數量 unsigned long length; //表中層數最大的節點的層數 int level; }
結構圖以下:
當一個集合的元素數量不大,且類型都爲整數時,redis就會使用整數集合進行存儲。它的結構以下:
typedef struct intset{ //編碼方式 uint32_t enconding; // 集合包含的元素數量 uint32_t length; //保存元素的數組 int8_t contents[]; }
壓縮列表是列表對象和哈希對象的實現方式之一。當一個列表的元素數量很少,且值爲整數和不長的字符串,這時redis會使用壓縮列表。一樣若是一個哈希對象的key和value的值數量很少,且爲整數和不長的字符串時,redis也會使用壓縮列表
參考資料: