Redis實現(xiàn)數(shù)據(jù)庫key:value存儲時夺鲜,基于基本數(shù)據(jù)結(jié)構(gòu)創(chuàng)建了一個對象系統(tǒng)镰踏,該系統(tǒng)包含了string, list, hash, set, zset五種類型的對象,每種都由redisObject表示当犯,type表示了對象的類型征椒,encoding表示對象的編碼方式,ptr指針指向了底層相應的基礎數(shù)據(jù)結(jié)構(gòu)末荐。
typedef struct redisObject {
//類型
unsigned type:4;
unsigned notused:2;
//編碼
unsigned encoding:4;
unsigned lru:22;
//引用計數(shù)
int refcount;
//指向底層實現(xiàn)數(shù)據(jù)結(jié)構(gòu)的指針
void *ptr;
} robj;
1.string
string的類型為REDIS_STRING常量,編碼有int, raw和embstr三種方式侧纯。
1.1. int
若一個字符串保存的是整數(shù)值而且可以用long類型表示,那么對象的ptr屬性將存放整數(shù)值(將void * 轉(zhuǎn)為long)甲脏, 同時將encoding設為int
1.2. raw
若字符串對象要保存長度大于32字節(jié)的字符串值眶熬,那么將使用SDS保存該這個值,同時將encoding設置為raw
1.3. embstr
embstr是用于保存短字符串的優(yōu)化編碼方式块请,雖然同樣使用SDS保存字符串值娜氏,但是僅調(diào)用一次內(nèi)存分配函數(shù),分配一塊連續(xù)的內(nèi)存空間給redisObject和SDS墩新。
2.list
list的類型為REDIS_LIST常量贸弥,編碼可以是ziplist和linkedlist
1. ziplist
使用壓縮列表作為底層實現(xiàn),每個entry保存一個元素
redis> RPUSH numbers 1 "three" 5
(integer 3)
2. linkedlist
使用雙向鏈表作為實現(xiàn)抖棘, 每個節(jié)點保存一個字符串對象茂腥,字符串對象保存一個列表元素狸涌。
3.編碼轉(zhuǎn)換
同時滿足以下條件可使用ziplist,否則使用linkedlist
- 所有元素字節(jié)數(shù)少于64
- 列表元素數(shù)量少于512
3. hash對象
hash的類型為REDIS_HASH常量最岗,編碼可以是ziplist和hashtable
1. ziplist
redis> HSET x name "Tom"
(integer) 1
redis> HSET x age 25
// ...
redis> HSET x career "Programmer"
// ...
每次在加入一對新的key:value時帕胆,先將key push到列表尾端,再將value push到列表尾端般渡。
2. hashtable
底層使用字典實現(xiàn),key 和 value 都是 字符串對象懒豹, 對象使用相應編碼保存字符串的值
3.編碼轉(zhuǎn)換
滿足以下情況時,可以將hashtable編碼轉(zhuǎn)為ziplist編碼驯用,否則都需要使用hashtable:
- 鍵值對數(shù)量小于512個
- 鍵脸秽、值的字符串長度都小于64字節(jié)
4. set
類型為REDIS_SET, 編碼可以是intset和hashtable
1. intset
intset底層使用整數(shù)集合保存集合對象的元素蝴乔。
2. hashtable
hashtable類型的集合底層使用字典實現(xiàn)记餐,其中字典的key使用字符串對象保存元素值,value為空薇正。
3. 編碼轉(zhuǎn)換
同時滿足以下兩個條件是片酝,可以使用intset編碼
1. 所有元素都是整數(shù)值
2. 元素數(shù)量少于512
5. zset
類型為REDIS_ZSET, 編碼可以為ziplist和skiplist
5.1ziplist
壓縮列表內(nèi)挖腰,使用相連的兩個壓縮列表節(jié)點分別存儲member和score雕沿;元素按score排序,大的靠近表位猴仑,小的靠近表頭审轮。
redis> ZADD price 8.5 apple 5.0 banana 6.5 cherry
添加三個元素后ziplist如圖
5.2 skiplist
skiplist編碼底層使用zset實現(xiàn)
typedef struct zset{
zskiplist *zsl;
dict *dict;
}
5.2.1zsl
zsl跳躍表按score從小到大保存所有元素,每個跳躍表節(jié)點保存一個元素.其中ele和score分別表示元素的成員和分值辽俗。
typedef struct zskiplistNode {
/* redis3.0版本中使用robj類型表示疾渣,但是在redis4.0.1中直接使用sds類型表示 */
sds ele;
double score;
struct zskiplistNode *backward;
/** 這里該成員是一種柔性數(shù)組,只是起到了占位符的作用,在sizeof(struct zskiplistNode)的時候根本就不占空間,這和sdshdr結(jié)構(gòu)的定義是類似的(sds. h文件)榆苞; 如果想要分配一個struct zskiplistNode大小的空間稳衬,那么應該的分配的大小為sizeof(struct zskiplistNode) + sizeof(struct zskiplistLevel) * count)。其中count為柔性數(shù)組中的元素的數(shù)量
**/
struct zskiplistLevel {
/* 對應level的下一個節(jié)點 */
struct zskiplistNode *forward;
/* 從當前節(jié)點到下一個節(jié)點的跨度 */
unsigned int span;
} level[];
} zskiplistNode;
通過跳躍表坐漏,可以高效實現(xiàn)范圍型操作薄疚,如ZRANGE, ZRANK赊琳。
5.2.2dict
dict提供了成員到score的映射街夭,因此可以高效的查找某個成員的score。
dict與zsl共享成員和score躏筏,因此內(nèi)存開銷并不大
3.編碼轉(zhuǎn)換:
同時滿足以下條件時板丽,使用ziplist,否則轉(zhuǎn)skiplist
- 元素數(shù)量少于128
- 所有元素成員字節(jié)數(shù)都少于64字節(jié)