整數集合
整數集合(intset)是redis用於保存整數值的集合抽象數據結構,它可以保存類型爲int16_t、int32_t、int64_t的整數值,並且保證集合中不會出現重複元素。
數據結構:
typedef struct intset {
uint32_t encoding;//INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64
uint32_t length; //記錄數組包含元素數量
int8_t contents[];
} intset;
contents數組是整個集合的底層實現:整數集合的每個元素都是contents數組的一個數組項(item), 各個項在數組中按值的大小從小到大有序排列。、
升級:
content保存着原來數組,如果添加進來一個數據長度要大於之前數組長度,就會升級
升級步驟:
1 根據新元素類型,擴展整數集合底層數組的空間大小,併爲新元素分配空間
2 將底層數組現有的所有元素都轉換成與新元素相同的類型,並將類型轉換後的元素放置在正確的位置上,而且在放置元素的過程中,需要繼續維持底層數組的有序性質不變
3 將新元素添加到底層數組裏面
//升級函數
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
uint8_t curenc = intrev32ifbe(is->encoding);
uint8_t newenc = _intsetValueEncoding(value);
int length = intrev32ifbe(is->length);
int prepend = value < 0 ? 1 : 0;
/* First set new encoding and resize */
is->encoding = intrev32ifbe(newenc);
is = intsetResize(is,intrev32ifbe(is->length)+1);
/* Upgrade back-to-front so we don't overwrite values.
* Note that the "prepend" variable is used to make sure we have an empty
* space at either the beginning or the end of the intset. */
while(length--)
_intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
/* Set the value at the beginning or the end. */
if (prepend)
_intsetSet(is,0,value);
else
_intsetSet(is,intrev32ifbe(is->length),value);
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}
//添加
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 1;
/* Upgrade encoding if necessary. If we need to upgrade, we know that
* this value should be either appended (if > 0) or prepended (if < 0),
* because it lies outside the range of existing values. */
if (valenc > intrev32ifbe(is->encoding)) {
/* This always succeeds, so we don't need to curry *success. */
//編碼大於之前,需要升級
return intsetUpgradeAndAdd(is,value);
} else {
/* Abort if the value is already present in the set.
* This call will populate "pos" with the right position to insert
* the value when it cannot be found. */
//找到最佳位置
if (intsetSearch(is,value,&pos)) {
if (success) *success = 0;
return is;
}
//擴展大小
is = intsetResize(is,intrev32ifbe(is->length)+1);
//move
if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
}
//set
_intsetSet(is,pos,value);
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
return is;
}
壓縮列表
壓縮列表(ziplist)是列表鍵和哈希鍵的底層實現之一。當一個列表鍵只包含少量列表項,並且每個列表項要麼就是小整數值,要麼就是長度比較短的字符串,那麼redis就使用壓縮列表來做列表鍵的底層實現,另外,當一個哈希鍵只包含少量鍵值對,並且每個鍵值對的鍵和值要麼就是小整數值,要麼就是長度比較短的字符串,那麼redis會使用壓縮列表做哈希表的底層實現。
域 |
長度/類型 |
域的值 |
zlbytes |
uint32_t |
整個 ziplist 佔用的內存字節數,對 ziplist 進行內存重分配,或者計算末端時使用。 |
zltail |
uint32_t |
到達 ziplist 表尾節點的偏移量。 通過這個偏移量,可以在不遍歷整個 ziplist 的前提下,彈出表尾節點。 |
zllen |
uint16_t |
ziplist 中節點的數量。 當這個值小於 UINT16_MAX (65535)時,這個值就是 ziplist 中節點的數量; 當這個值等於 UINT16_MAX 時,節點的數量需要遍歷整個 ziplist 才能計算得出。 |
entryX |
? |
ziplist 所保存的節點,各個節點的長度根據內容而定。 |
zlend |
uint8_t |
255 的二進制值 1111 1111 (UINT8_MAX) ,用於標記 ziplist 的末端。 |
對象
redis並沒有直接使用這些原始的數據結構,而是把這些數據結構封裝成對象。這個系統包含字符串對象、列表對象、哈希對象、集合對象、有序集合對象, 這些對象會根據不同場景使用不同數據結構
typedef struct redisObject {
unsigned type:4; //對象類型
unsigned encoding:4; //對象使用編碼
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr; //指向對象的底層數據結構
} robj;
類型:
對象 |
對象type屬性的值 |
type命令的輸出 |
字符串對象 |
REDIS_STRING |
“string”’ |
列表對象 |
REDIS_LIST |
“list” |
哈希對象 |
REDIS_HASH |
“hash” |
集合對象 |
REDIS_SET |
“set” |
有序集合對象 |
REDIS_HSET |
“zset” |
編碼:
編碼常量 |
底層數據結構 |
|
REDIS_ENCODING_INT |
long類型的整數 |
REDIS_STRING |
REDIS_ENCODING_EMBSTR |
embstr編碼的簡單動態字符串 |
|
REDIS_ENCODING_RAW |
簡單動態字符串 |
|
REDIS_ENCODING_HT |
字典 |
REDIS_HASH REDIS_SET |
REDIS_ENCODING_LINKEDLIST |
雙端鏈表 |
REDIS_LIST |
REDIS_ENCODING_ZIPLIST |
壓縮列表 |
REDIS_LIST REDIS_HASH REDIS_ZSET |
REDIS_ENCODING_INTSET |
整數集合 |
REDIS_SET |
REDIS_ENCODING_SKIPLIST |
跳躍表和字典 |
REDIS_ZSET |
對redis數據庫保存的鍵值對來說,鍵總是一個字符串對象,而值可以是字符串對象、列表對象、哈希對象、集合對象、有序集合對象的其中一種。
通過encoding屬性來設定對象所使用的編碼,而不是爲特定類型的對象關聯一種固定的編碼,極大的提升了redis的靈活性和效率。
有序集合zset 是dict和skiplist結合 dict保存key->score skiplist保存zset 數據