redis 是 key-value 存儲系統(tǒng),其中 key 類型一般為字符串,而 value 類型則為 redis 對象(redis object)。Redis 對象可以綁定各種類型的數(shù)據(jù),譬如 string、list 和set。
typedef struct redisObject {
// 剛剛好32 bits
// 對象的類型,字符串/列表/集合/哈希表
unsigned type:4;
// 未使用的兩個位
unsigned notused:2; /* Not used */
// 編碼的方式,Redis 為了節(jié)省空間,提供多種方式來保存一個數(shù)據(jù)
// 譬如:“123456789” 會被存儲為整數(shù)123456789
unsigned encoding:4;
// 當(dāng)內(nèi)存緊張,淘汰數(shù)據(jù)的時候用到
unsigned lru:22; /* lru time (relative to server.lruclock) */
// 引用計數(shù)
int refcount;
// 數(shù)據(jù)指針
void *ptr;
} robj;
其中,void *ptr 已經(jīng)給了我們無限的遐想空間了。
redis.h 中定義了 struct redisObject,它是一個簡單優(yōu)秀的數(shù)據(jù)結(jié)構(gòu),因為在 redisObject 中數(shù)據(jù)屬性和數(shù)據(jù)分開來了,其中,數(shù)據(jù)屬性包括數(shù)據(jù)類型,存儲編碼方式,淘汰時鐘,引用計數(shù)。下面一一展開:
數(shù)據(jù)類型,標(biāo)記了 Redis 對象綁定的是什么類型的數(shù)據(jù),有下面幾種可能的值;
/* Object types */
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
存儲編碼方式,一個數(shù)據(jù),可以以多種方式存儲。譬如,數(shù)據(jù)類型為 REDIS_SET 的數(shù)據(jù)編碼方式可能為 REDIS_ENCODING_HT,也可能為 REDIS_ENCODING_INTSET。
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
#define REDIS_ENCODING_RAW 0 /* Raw representation */
#define REDIS_ENCODING_INT 1 /* Encoded as integer */
#define REDIS_ENCODING_HT 2 /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
淘汰時鐘,Redis 對數(shù)據(jù)集占用內(nèi)存的大小有「實時」的計算,當(dāng)超出限額時,會淘汰超時的數(shù)據(jù)。
引用計數(shù),一個 Redis 對象可能被多個指針引用。當(dāng)需要增加或者減少引用的時候,必須調(diào)用相應(yīng)的函數(shù),程序員必須遵守這一準(zhǔn)則。
// 增加 Redis 對象引用
void incrRefCount(robj *o) {
o->refcount++;
}
// 減少 Redis 對象引用。特別的,引用為零的時候會銷毀對象
void decrRefCount(robj *o) {
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
// 如果取消的是最后一個引用,則釋放資源
if (o->refcount == 1) {
// 不同數(shù)據(jù)類型,銷毀操作不同
switch(o->type) {
case REDIS_STRING: freeStringObject(o); break;
case REDIS_LIST: freeListObject(o); break;
case REDIS_SET: freeSetObject(o); break;
case REDIS_ZSET: freeZsetObject(o); break;
case REDIS_HASH: freeHashObject(o); break;
default: redisPanic("Unknown object type"); break;
}
zfree(o);
} else {
o->refcount--;
}
}
得益于 Redis 是單進(jìn)程單線程工作的,所以增加/減少引用的操作不必保證原子性,這在 memcache 中是做不到的(memcached 是多線程的工作模式,需要做到互斥)。structredisObject 把最后一個指針留給了真正的數(shù)據(jù)。