redis中实现很多数据结构来存储键值对,主要有简单动态字符串、双端队列、字典、压缩列表、整数集合和跳跃表。但是使用也是基于这些数据结构构建看一个对象系统,主要是字符串对象、列表对象、哈希对象、集合对象和有序集合对象,每种对象都用到至少一种上述的数据结构。
在object.c主要是实现了对象的创建、引用计数和释放,字符串对象的编码转换。
对象的结构体
typedef struct redisObject {
unsigned type:4;//对象类型
unsigned encoding:4;//对象的编码类型
unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;//对象的引用计数
void *ptr;//指向对象内容的指针
} robj;
对象的五种类型
#define OBJ_STRING 0//字符串对象
#define OBJ_LIST 1//列表对象
#define OBJ_SET 2//集合对象
#define OBJ_ZSET 3//有序集合对象
#define OBJ_HASH 4//哈希对象
对象的编码
#define OBJ_ENCODING_RAW 0//简单动态字符串——字符串对象
#define OBJ_ENCODING_INT 1//long类型整数——字符串对象
#define OBJ_ENCODING_HT 2//字典——散列对象和集合对象
#define OBJ_ENCODING_ZIPMAP 3//压缩图
#define OBJ_ENCODING_LINKEDLIST 4 //双端列表——列表对象
#define OBJ_ENCODING_ZIPLIST 5 //压缩列表——有序集合对象
#define OBJ_ENCODING_INTSET 6 //整数集合——集合对象
#define OBJ_ENCODING_SKIPLIST 7 //跳跃表——有序集合对象
#define OBJ_ENCODING_EMBSTR 8 //embstr编码的简单动态字符串——字符串对象
#define OBJ_ENCODING_QUICKLIST 9 //基于压缩列表的双端列表——列表对象
一、对象的创建
基本对象创建函数,创建robj结构体,指定类型和对象内容,编码初始化为OBJ_ENCODING_RAW,泛化的对象创建函数需要重新设定自己的编码。
robj *createObject(int type, void *ptr) {
robj *o = zmalloc(sizeof(*o));
o->type = type;
o->encoding = OBJ_ENCODING_RAW;
o->ptr = ptr;
o->refcount = 1;
/* Set the LRU to the current lruclock (minutes resolution). */
o->lru = LRU_CLOCK();
return o;
}
五种对象的创建函数
//根据字符串长度选择使用OBJ_ENCODING_EMBSTR还是OBJ_ENCODING_RAW创建字符串对象
robj *createStringObject(const char *ptr, size_t len) {
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
robj *createEmbeddedStringObject(const char *ptr, size_t len);//创建OBJ_ENCODING_EMBSTR编码的字符串对象
robj *createRawStringObject(const char *ptr, size_t len);//封装createObject创建OBJ_ENCODING_RAW编码的列表对象
robj *createQuicklistObject(void);//封装createObject创建OBJ_ENCODING_QUICKLIST编码的列表对象
robj *createZiplistObject(void);//封装createObject创建OBJ_ENCODING_ZIPLIST编码的列表对象
robj *createSetObject(void);//封装createObject创建OBJ_ENCODING_HT编码的集合对象
robj *createIntsetObject(void);//封装createObject创建OBJ_ENCODING_INTSET编码的集合对象
robj *createHashObject(void);//封装createObject创建OBJ_ENCODING_ZIPLIST编码的散列对象
robj *createZsetObject(void);//封装createObject创建OBJ_ENCODING_ZIPLIST编码的有序集合对象
robj *createZsetZiplistObject(void);//封装createObject创建OBJ_ENCODING_ZIPLIST编码的有序集合对象
二、对象的引用计数和释放
对象的引用计数主要是为了更好的管理对象,及时的释放掉对象不用的内存。
void incrRefCount(robj *o) {//引用计数增加函数
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++;
}//引用计数减少函数
void decrRefCountVoid(void *o) {
decrRefCount(o);
}
//引用计数减少函数,当计数为1是调用,释放对象。
void decrRefCount(robj *o) {
if (o->refcount == 1) {
switch(o->type) {//根据类型不同选择不同释放函数
case OBJ_STRING: freeStringObject(o); break;
case OBJ_LIST: freeListObject(o); break;
case OBJ_SET: freeSetObject(o); break;
case OBJ_ZSET: freeZsetObject(o); break;
case OBJ_HASH: freeHashObject(o); break;
default: serverPanic("Unknown object type"); break;
}
zfree(o);
} else {
if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;
}
}//重置对象引用计数
robj *resetRefCount(robj *obj) {
obj->refcount = 0;
return obj;
}
对象释放函数
void freeStringObject(robj *o); //释放字符串对象的内容,o->ptr
void freeListObject(robj *o); //释放列表对象的内容,o->ptr
void freeSetObject(robj *o); //释放集合对象的内容,o->ptr
void freeZsetObject(robj *o); //释放有序集合对象的内容,o->ptr
void freeHashObject(robj *o);//释放散列对象的内容,o->ptr
三、对象的编码转换
//对字符串对象进行节省空间的编码转换
robj *tryObjectEncoding(robj *o) {
long value;
sds s = o->ptr;
size_t len;
//OBJ_ENCODING_INT编码的对象不需要编码转换
if (!sdsEncodedObject(o)) return o;
//引用计数大于1的字符串对象不能进行编码转换
if (o->refcount > 1) return o;
len = sdslen(s);
if (len <= 21 && string2l(s,len,&value)) {
//内容为长度小于21的数字字符串的字符串对象
if ((server.maxmemory == 0 ||
(server.maxmemory_policy != MAXMEMORY_VOLATILE_LRU &&
server.maxmemory_policy != MAXMEMORY_ALLKEYS_LRU)) &&
value >= 0 &&
value < OBJ_SHARED_INTEGERS)
{/*当内存策略不是lru或没有可使用内存和数字介于
0~OBJ_SHARED_INTEGERS使用共享整数字符串,不改变编码*/
decrRefCount(o);
incrRefCount(shared.integers[value]);
return shared.integers[value];
} else {//编码转换成OBJ_ENCODING_INT
if (o->encoding == OBJ_ENCODING_RAW) sdsfree(o->ptr);
o->encoding = OBJ_ENCODING_INT;
o->ptr = (void*) value;
return o;
}
}
//字符串长度小于OBJ_ENCODING_EMBSTR_SIZE_LIMIT,编码转换成OBJ_ENCODING_EMBSTR
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT,编码转换成) {
robj *emb;
if (o->encoding == OBJ_ENCODING_EMBSTR) return o;
emb = createEmbeddedStringObject(s,sdslen(s));
decrRefCount(o);
return emb;
}
//编码为OBJ_ENCODING_RAW,sds的可用大小大于len/10,释放掉可用空间
if (o->encoding == OBJ_ENCODING_RAW &&
sdsavail(s) > len/10)
{
o->ptr = sdsRemoveFreeSpace(o->ptr);
}
return o;
}
//将OBJ_ENCODING_INT编码字符串转成OBJ_ENCODING_EMBSTR或者OBJ_ENCODING_RAW
robj *getDecodedObject(robj *o) {
robj *dec;
if (sdsEncodedObject(o)) {
incrRefCount(o);
return o;
}
if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {
char buf[32];
ll2string(buf,32,(long)o->ptr);
dec = createStringObject(buf,strlen(buf));
return dec;
} else {
serverPanic("Unknown encoding type");
}
}