redis 是 key-value 存儲(chǔ)系統(tǒng),其中 key 類型一般為字符串,而 value 類型則為 redis 對(duì)象(redis object)。Redis 對(duì)象可以綁定各種類型的數(shù)據(jù),譬如 string、list 和set。
typedef struct redisObject {
// 剛剛好32 bits
// 對(duì)象的類型,字符串/列表/集合/哈希表
unsigned type:4;
// 未使用的兩個(gè)位
unsigned notused:2; /* Not used */
// 編碼的方式,Redis 為了節(jié)省空間,提供多種方式來(lái)保存一個(gè)數(shù)據(jù)
// 譬如:“123456789” 會(huì)被存儲(chǔ)為整數(shù)123456789
unsigned encoding:4;
// 當(dāng)內(nèi)存緊張,淘汰數(shù)據(jù)的時(shí)候用到
unsigned lru:22; /* lru time (relative to server.lruclock) */
// 引用計(jì)數(shù)
int refcount;
// 數(shù)據(jù)指針
void *ptr;
} robj;
其中,void *ptr 已經(jīng)給了我們無(wú)限的遐想空間了。
redis.h 中定義了 struct redisObject,它是一個(gè)簡(jiǎn)單優(yōu)秀的數(shù)據(jù)結(jié)構(gòu),因?yàn)樵?redisObject 中數(shù)據(jù)屬性和數(shù)據(jù)分開(kāi)來(lái)了,其中,數(shù)據(jù)屬性包括數(shù)據(jù)類型,存儲(chǔ)編碼方式,淘汰時(shí)鐘,引用計(jì)數(shù)。下面一一展開(kāi):
數(shù)據(jù)類型,標(biāo)記了 Redis 對(duì)象綁定的是什么類型的數(shù)據(jù),有下面幾種可能的值;
/* Object types */
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
存儲(chǔ)編碼方式,一個(gè)數(shù)據(jù),可以以多種方式存儲(chǔ)。譬如,數(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 */
淘汰時(shí)鐘,Redis 對(duì)數(shù)據(jù)集占用內(nèi)存的大小有「實(shí)時(shí)」的計(jì)算,當(dāng)超出限額時(shí),會(huì)淘汰超時(shí)的數(shù)據(jù)。
引用計(jì)數(shù),一個(gè) Redis 對(duì)象可能被多個(gè)指針引用。當(dāng)需要增加或者減少引用的時(shí)候,必須調(diào)用相應(yīng)的函數(shù),程序員必須遵守這一準(zhǔn)則。
// 增加 Redis 對(duì)象引用
void incrRefCount(robj *o) {
o->refcount++;
}
// 減少 Redis 對(duì)象引用。特別的,引用為零的時(shí)候會(huì)銷毀對(duì)象
void decrRefCount(robj *o) {
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
// 如果取消的是最后一個(gè)引用,則釋放資源
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 把最后一個(gè)指針留給了真正的數(shù)據(jù)。