1、Redis中的object
为了便于操作,Redis定义了redisObjec结构体来表示string、hash、list、set、zset五种数据类型。redisObject定义在redis.h文件中:
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;
其中各字段的含义如下:
(1)、4位的type表示具体的数据类型。Redis中共有5中数据类型。2^4 = 8足以表示这些类型。
/* Object types */
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
4位的encoding表示该类型的物理编码方式,同一种数据类型可能有不同的编码方式。目前Redis中主要有8种编码方式:
/* 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 */
(3)、lru字段表示当内存超限时采用LRU算法清除内存中的对象。
(4)、refcount表示对象的引用计数。
(5)、ptr指针指向真正的存储结构。
那么我们怎么来理解redisObjec结构的作用呢?为了便于操作,Redis采用redisObjec结构来统一五种不同的数据类型,这样所有的数据类型就都可以以相同的形式在函数间传递而不用使用特定的类型结构。同时,为了识别不同的数据类型,redisObjec中定义了type和encoding字段对不同的数据类型加以区别。简单地说,redisObjec就是string、hash、list、set、zset的父类,可以在函数间传递时隐藏具体的类型信息,只是C语言中没有“继承”的概念,所以作者抽象了redisObjec结构来到达同样的目的。
字符串string类型可以被编码为 raw (常规字符串) 或者int (用字符串表示64位无符号整数这种编码方式是为了节省空间)。
哈希表Hash类型可以被编码为 ziplist 或者hash_table(就是字典dict)。ziplist 是为了节省列表空间而设计一种特殊编码方式,Redis将两个相邻的节点看作一个键值对从而模拟map结构。
列表list类型可以被编码为ziplist 或者 linkedlist。 ziplist 是为了节省列表空间而设计一种特殊编码方式。
集合set类型被编码为 intset 或者 hash_table。 intset 是为了存储数字的较小集合而设计的一种特殊编码方式。
有序集合被编码为ziplist 或者 skiplist 格式。ziplist可以表示较小的有序集合, skiplist表示任意大小多的有序集合。
对象创建
robj *createObject(int type, void *ptr);
robj *createStringObject(char *ptr, size_t len);
robj *createStringObjectFromLongLong(long long value);
robj *createStringObjectFromLongDouble(long double value, int humanfriendly);
robj *createListObject(void);
robj *createZiplistObject(void);
robj *createSetObject(void);
robj *createIntsetObject(void);
robj *createHashObject(void);
robj *createZsetObject(void);
robj *createZsetZiplistObject(void);
createXXXObject的方法主要通过调用底层结构的创建方法来创建一个robj对象,例如createZiplistObject方法调用了ziplistNew方法创建一个ziplist然后赋值给robj.ptr指针。其它的就不一一赘述。
/* 创建一个ziplist对象 */
robj *createZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(REDIS_LIST,zl);
o->encoding = REDIS_ENCODING_ZIPLIST;
return o;
}
对象的释放
void freeStringObject(robj *o);
void freeListObject(robj *o);
void freeSetObject(robj *o);
void freeZsetObject(robj *o);
void freeHashObject(robj *o);
对象的释放也是通过调用底层结构的方法来释放资源的,不同的是在释放前需要判断该对象的具体编码方式(然后进行相应的释放操作),如freeListObject:
/* 释放一个list对象 */
void freeListObject(robj *o) {
// list有两种不同的实现,根据不同的实现释放资源
switch (o->encoding) {
case REDIS_ENCODING_LINKEDLIST:
listRelease((list*) o->ptr);
break;
case REDIS_ENCODING_ZIPLIST:
zfree(o->ptr);
break;
default:
redisPanic("Unknown list encoding type");
}
}
对象的引用计数操作
Redis采用“引用计数法”来管理对象,使用incrRefCount来增加对象的引用计数值,使用decrRefCount来减少对象的引用计数值(如果对象的引用计数值减为0则销毁之)。
void decrRefCount(robj *o);
void incrRefCount(robj *o);