/* A redis object, that is a type able to hold a string / list / set */
redis对象,是用来保存 字符串,列表,集合的一种类型。
个人理解就是对这些不同的类型做一个统一的封装,这样对外面调用比较友好
/* The actual Redis Object */
#define OBJ_STRING 0 /* String object. */
#define OBJ_LIST 1 /* List object. */
#define OBJ_SET 2 /* Set object. */
#define OBJ_ZSET 3 /* Sorted set object. */
#define OBJ_HASH 4 /* Hash object. */
/* The "module" object type is a special one that signals that the object
* is one directly managed by a Redis module. In this case the value points
* to a moduleValue struct, which contains the object value (which is only
* handled by the module itself) and the RedisModuleType struct which lists
* function pointers in order to serialize, deserialize, AOF-rewrite and
* free the object.
模块对象类型是一种特殊的类型,意味着由这个对象是一个直接由REDIS模块管理的。
在这种情况下,这个值指向一个包含对象值的模块值结构(只能由模块本身处理)和这个redis模块类型结构,
这个类型结构列出了为了序列化,反序列化,AOF重写和释放对象的函数指针。
* Inside the RDB file, module types are encoded as OBJ_MODULE followed
* by a 64 bit module type ID, which has a 54 bits module-specific signature
* in order to dispatch the loading to the right module, plus a 10 bits
* encoding version. */
在备份文件RDB文件中,模块类型都被编码为OBJ_MODULE类型,尾随着一个64比特的模块类型ID,
由54bit模块特定签名决定分配真确的模块方法去加载,外加一个10比特的编码版本
#define OBJ_MODULE 5 /* Module object. */
#define OBJ_STREAM 6 /* Stream object. */
/* 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 OBJ_ENCODING_RAW 0 /* Raw representation */
#define OBJ_ENCODING_INT 1 /* Encoded as integer */
#define OBJ_ENCODING_HT 2 /* Encoded as hash table */
#define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
#define LRU_BITS 24
#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */
#define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */
#define OBJ_SHARED_REFCOUNT INT_MAX /* Global object never destroyed. */
#define OBJ_STATIC_REFCOUNT (INT_MAX-1) /* Object allocated in the stack. */
#define OBJ_FIRST_SPECIAL_REFCOUNT OBJ_STATIC_REFCOUNT
typedef struct redisObject {
unsigned type:4; //类型
unsigned encoding:4; //编码方式,就是存储方式
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
//这个地段需要根据不同的淘汰算法来决定,如果使用LRU,那么就是一个时间值(和全局lru_clock相关)
//或者LFU数据(最低8位表示频率同时最高16位表示存取时间)
int refcount; //引用次数,次数为了表示没有引用,可以释放
void *ptr; //指向对象保存的数据
} robj;
***************************************************************************************
/* The a string name for an object's type as listed above
* Native types are checked against the OBJ_STRING, OBJ_LIST, OBJ_* defines,
* and Module types have their registered name returned. */
每个对象的类型有一个字符串名字如上表所示(之前定义的,即下面的类型)
/* The actual Redis Object */
#define OBJ_STRING 0 /* String object. */
#define OBJ_LIST 1 /* List object. */
#define OBJ_SET 2 /* Set object. */
#define OBJ_ZSET 3 /* Sorted set object. */
#define OBJ_HASH 4 /* Hash object. */
#define OBJ_MODULE 5 /* Module object. */
#define OBJ_STREAM 6 /* Stream object. */)
原始类型都被检测使用类似 OBJ_STRING, OBJ_LIST, OBJ_* 的定义,模块的类型根据它们注册名返回
********************************************************************************************
char* getObjectTypeName(robj *o) {
char* type;
if (o == NULL) {
type = "none"; //对象为空
} else {
switch(o->type) {
case OBJ_STRING: type = "string"; break;
case OBJ_LIST: type = "list"; break;
case OBJ_SET: type = "set"; break;
case OBJ_ZSET: type = "zset"; break;
case OBJ_HASH: type = "hash"; break;
case OBJ_STREAM: type = "stream"; break;
//非模块对象 比较直接,如上所示
//模块对象比较特殊,需要获取具体的值,然后从值中获取名称
case OBJ_MODULE: {
moduleValue *mv = o->ptr;
type = mv->type->name;
}; break;
default: type = "unknown"; break;
}
}
return type;
}
/* In Redis objects 'robj' structures of type OBJ_MODULE, the value pointer
* is set to the following structure, referencing the moduleType structure
* in order to work with the value, and at the same time providing a raw
* pointer to the value, as created by the module commands operating with
* the module type.
在OBJ_MODULE类型的Redis objects'robj'结构中,值指针被设置为以下结构,
引用moduleType结构来处理该值,同时提供一个原始的值指针,
就如操作模块类型时候创建的模块命令(我个人的理解是如同redisObject中的void *ptr;)
* So for example in order to free such a value, it is possible to use
* the following code:
所以如果想要释放这样一个值,作为一个例子,我们可以采用如下的代码
* if (robj->type == OBJ_MODULE) { //是模块类型
* moduleValue *mt = robj->ptr; //取出具体的内容
* mt->type->free(mt->value); //释放内容中分配的内存
* zfree(mt); // We need to release this in-the-middle struct as well.
//释放redisObject中的void *ptr,因为这个指针指向的内容也需要分配获取
* }
*/
typedef struct moduleValue {
moduleType *type; //模块类型
void *value; //模块具体的值
} moduleValue;
/* The module type, which is referenced in each value of a given type, defines
* the methods and links to the module exporting the type. */
对于每个给定类型引用的每个值的模块类型,定义方法和连接供模块外部使用
typedef struct RedisModuleType {
uint64_t id; /* Higher 54 bits of type ID + 10 lower bits of encoding ver. */
struct RedisModule *module;
moduleTypeLoadFunc rdb_load;
moduleTypeSaveFunc rdb_save;
moduleTypeRewriteFunc aof_rewrite;
moduleTypeMemUsageFunc mem_usage;
moduleTypeDigestFunc digest;
moduleTypeFreeFunc free;
moduleTypeAuxLoadFunc aux_load;
moduleTypeAuxSaveFunc aux_save;
int aux_save_triggers;
char name[10]; /* 9 bytes name + null term. Charset: A-Z a-z 0-9 _- */
//这里说明模块名字的长度为9个字符
} moduleType;
**************************************************************************************
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; //应用计算置成1
/* Set the LRU to the current lruclock (minutes resolution), or
* alternatively the LFU counter. */
根据不同的淘汰算法设置不同的淘汰标志,时间(LRU)或者时间加频率(LFU)
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
}
return o;
}
**************************************************************************************
/* Set a special refcount in the object to make it "shared":
* incrRefCount and decrRefCount() will test for this special refcount
* and will not touch the object. This way it is free to access shared
* objects such as small integers from different threads without any
* mutex.
在对象内设置一个特殊的引用使得它可以被共享:
函数incrRefCount和decrRefCount()利用特殊引用做测试,不需要直接操作对象内容。
这种方法可以自由访问共享对象,比如不同线程间的小整数而不需要任何互斥。
* A common patter to create shared objects:
一个通用模式来创建共享对象
* robj *myobject = makeObjectShared(createObject(...));
*
*/
robj *makeObjectShared(robj *o) {
serverAssert(o->refcount == 1);
o->refcount = OBJ_SHARED_REFCOUNT; //设置为共享计数
return o;
}
#define OBJ_STATIC_REFCOUNT (INT_MAX-1) /* Object allocated in the stack. */
#define OBJ_FIRST_SPECIAL_REFCOUNT OBJ_STATIC_REFCOUNT
void incrRefCount(robj *o) {
if (o->refcount < OBJ_FIRST_SPECIAL_REFCOUNT) {
o->refcount++; //引用计数++
} else {
if (o->refcount == OBJ_SHARED_REFCOUNT) { //如果已经是共享,不需要做任何操作
/* Nothing to do: this refcount is immutable. */
} else if (o->refcount == OBJ_STATIC_REFCOUNT) { //如果已经是共享计数减一的情况,只给提示,不做操作
serverPanic("You tried to retain an object allocated in the stack");
}
}
}
对于减少计数的情况
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;
case OBJ_MODULE: freeModuleObject(o); break;
case OBJ_STREAM: freeStreamObject(o); break;
default: serverPanic("Unknown object type"); break;
}
zfree(o); //释放对象结构体
} else {
if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0"); /如果引用计数小于等于0,给提示
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--; //最正常的情况,引用计数减去1
}
}
**************************************************************************************
/* Create a string object with EMBSTR encoding if it is smaller than
* OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
* used.
创建一个嵌入式编码的字符串,如果该字符串的长度小于44个字节,否则需要创建一个原始编码的字符串
* The current limit of 44 is chosen so that the biggest string object
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
如果当前限制44被选择,那么我们为最大嵌入式字符串对象分配的内存仍然适合jemalloc关于64字节的内存策略,
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44 //最大能存储的字符串长度
robj *createStringObject(const char *ptr, size_t len) {
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) //小于等于44,可以采用嵌入式字符串
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);//大于44,采用独立的原始SDS字符串
}
因为struct redisObject的size为16个字节,struct sdshdr8为3个字节,加上一个结尾'\0',结构需要20个字节
所以总的可以用的字节数为 64 -20 = 44,所以可用字节数为44个字节,即为上述判断的标准
16个字节
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
int refcount; //4
void *ptr; //8
} robj;
3个字节
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
**************************************************************************************
/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
* string object where o->ptr points to a proper sds string. */
创建一个编码类型为OBJ_ENCODING_RAW的字符串对象,
这是一个纯字符串对象,其指针o->ptr指向一个具体的sds字符串
robj *createRawStringObject(const char *ptr, size_t len) {
return createObject(OBJ_STRING, sdsnewlen(ptr,len));
}
/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
* an object where the sds string is actually an unmodifiable string
* allocated in the same chunk as the object itself. */
创建一个编码类型为OBJ_ENCODING_EMBSTR的字符串对象,这是一个对象,
其sds字符串实际是是一个不可改变的sds字符串,分配在同样对象同一块内存中
robj *createEmbeddedStringObject(const char *ptr, size_t len) {
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1); //这里就是分配了一整块内存
struct sdshdr8 *sh = (void*)(o+1); //sds结构体开始位置
o->type = OBJ_STRING;
o->encoding = OBJ_ENCODING_EMBSTR;
o->ptr = sh+1; //指向sds字符串的具体内容开始位置
o->refcount = 1;
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
}
sh->len = len;
sh->alloc = len;
sh->flags = SDS_TYPE_8;
if (ptr == SDS_NOINIT)
sh->buf[len] = '\0';
else if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = '\0';
} else {
memset(sh->buf,0,len+1); \\0等同于'\0',
}
return o;
}
**************************************************************************************
/* Create a string object from a long long value. When possible returns a
* shared integer object, or at least an integer encoded one.
从一个长整形的值创建一个字符串对象,可能返回一个共享的整型对象,或者至少返回一个整型编码的对象
* If valueobj is non zero, the function avoids returning a a shared
* integer, because the object is going to be used as value in the Redis key
* space (for instance when the INCR command is used), so we want LFU/LRU
* values specific for each key. */
如果valueobj的值非0,那么函数避免返回一个共享整型对象,因为这个对象会被当做值使用在redis的键空间,
(举例 当INCR命令被执行时),所以我们希望每个键都有一个LFU/LRU值
robj *createStringObjectFromLongLongWithOptions(long long value, int valueobj) {
robj *o;
if (server.maxmemory == 0 ||
!(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS))
{ //如果最大内存不做限制 或者 虽然有内存策略,但是不淘汰,这两种请情况下,我们还是可以使用共享对象
/* If the maxmemory policy permits, we can still return shared integers
* even if valueobj is true. */
valueobj = 0;
}
if (value >= 0 && value < OBJ_SHARED_INTEGERS && valueobj == 0) {
//值在0到OBJ_SHARED_INTEGERS之间,并且允许共享
incrRefCount(shared.integers[value]); //共享引用计数加1
o = shared.integers[value]; //返回共享对象指针
} else {
if (value >= LONG_MIN && value <= LONG_MAX) {
o = createObject(OBJ_STRING, NULL); //创建对象
o->encoding = OBJ_ENCODING_INT; //编码为整型
o->ptr = (void*)((long)value);
} else {
o = createObject(OBJ_STRING,sdsfromlonglong(value));
}
}
return o;
}
小整数的共享对象在初始化的时候生成
void createSharedObjects(void) {
...
for (j = 0; j < OBJ_SHARED_INTEGERS; j++) {
shared.integers[j] =
makeObjectShared(createObject(OBJ_STRING,(void*)(long)j));
shared.integers[j]->encoding = OBJ_ENCODING_INT;
}
...
}
**************************************************************************************
/* Wrapper for createStringObjectFromLongLongWithOptions() always demanding
* to create a shared object if possible. */
对函数createStringObjectFromLongLongWithOptions进行包装,尽可能创建一个共享对象
robj *createStringObjectFromLongLong(long long value) {
return createStringObjectFromLongLongWithOptions(value,0);
}
/* Wrapper for createStringObjectFromLongLongWithOptions() avoiding a shared
* object when LFU/LRU info are needed, that is, when the object is used
* as a value in the key space, and Redis is configured to evict based on
* LFU/LRU. */
对函数createStringObjectFromLongLongWithOptions进行包装避免成为一个共享对象,
因为需要淘汰策略信息(LFU/LRU),因为对象被当做一个值使用在键空间,
redis系统的键被配置成基于LFU/LRU的淘汰策略
robj *createStringObjectFromLongLongForValue(long long value) {
return createStringObjectFromLongLongWithOptions(value,1);
}
**************************************************************************************
/* Duplicate a string object, with the guarantee that the returned object
* has the same encoding as the original one.
复制一个字符串对象,保证返回的对象拥有和原始对象一致的编码。
* This function also guarantees that duplicating a small integer object
* (or a string object that contains a representation of a small integer)
* will always result in a fresh object that is unshared (refcount == 1).
这个函数也保证赋值一个小整型对象(或者 一个字符串对象 包含一个小整数的表示)
总是能保证返回一个新的不共享的对象
* The resulting object always has refcount set to 1. */
结果返回的对象总是有一个等于1的引用值
robj *dupStringObject(const robj *o) {
robj *d;
serverAssert(o->type == OBJ_STRING);
switch(o->encoding) {
case OBJ_ENCODING_RAW:
return createRawStringObject(o->ptr,sdslen(o->ptr));
case OBJ_ENCODING_EMBSTR:
return createEmbeddedStringObject(o->ptr,sdslen(o->ptr));
case OBJ_ENCODING_INT:
d = createObject(OBJ_STRING, NULL);
d->encoding = OBJ_ENCODING_INT;
d->ptr = o->ptr;
return d;
default:
serverPanic("Wrong encoding.");
break;
}
}
**************************************************************************************
创建各种类型的redis对象,统一化,使用指针指向具体的内容,外面看上去一致
robj *createQuicklistObject(void) {
quicklist *l = quicklistCreate();
robj *o = createObject(OBJ_LIST,l);
o->encoding = OBJ_ENCODING_QUICKLIST;
return o;
}
robj *createZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(OBJ_LIST,zl);
o->encoding = OBJ_ENCODING_ZIPLIST;
return o;
}
robj *createSetObject(void) {
dict *d = dictCreate(&setDictType,NULL);
robj *o = createObject(OBJ_SET,d);
o->encoding = OBJ_ENCODING_HT;
return o;
}
robj *createIntsetObject(void) {
intset *is = intsetNew();
robj *o = createObject(OBJ_SET,is);
o->encoding = OBJ_ENCODING_INTSET;
return o;
}
robj *createHashObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(OBJ_HASH, zl);
o->encoding = OBJ_ENCODING_ZIPLIST;
return o;
}
robj *createZsetObject(void) {
zset *zs = zmalloc(sizeof(*zs));
robj *o;
zs->dict = dictCreate(&zsetDictType,NULL);
zs->zsl = zslCreate();
o = createObject(OBJ_ZSET,zs);
o->encoding = OBJ_ENCODING_SKIPLIST;
return o;
}
robj *createZsetZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(OBJ_ZSET,zl);
o->encoding = OBJ_ENCODING_ZIPLIST;
return o;
}
robj *createStreamObject(void) {
stream *s = streamNew();
robj *o = createObject(OBJ_STREAM,s);
o->encoding = OBJ_ENCODING_STREAM;
return o;
}
robj *createModuleObject(moduleType *mt, void *value) {
moduleValue *mv = zmalloc(sizeof(*mv));
mv->type = mt;
mv->value = value;
return createObject(OBJ_MODULE,mv);
}
**************************************************************************************
释放redis对象,根据类型做不同的释放操作
void freeStringObject(robj *o) {
if (o->encoding == OBJ_ENCODING_RAW) {
sdsfree(o->ptr);
}
}
void freeListObject(robj *o) {
if (o->encoding == OBJ_ENCODING_QUICKLIST) {
quicklistRelease(o->ptr);
} else {
serverPanic("Unknown list encoding type");
}
}
void freeSetObject(robj *o) {
switch (o->encoding) {
case OBJ_ENCODING_HT:
dictRelease((dict*) o->ptr);
break;
case OBJ_ENCODING_INTSET:
zfree(o->ptr);
break;
default:
serverPanic("Unknown set encoding type");
}
}
void freeZsetObject(robj *o) {
zset *zs;
switch (o->encoding) {
case OBJ_ENCODING_SKIPLIST:
zs = o->ptr;
dictRelease(zs->dict);
zslFree(zs->zsl);
zfree(zs);
break;
case OBJ_ENCODING_ZIPLIST:
zfree(o->ptr);
break;
default:
serverPanic("Unknown sorted set encoding");
}
}
void freeHashObject(robj *o) {
switch (o->encoding) {
case OBJ_ENCODING_HT:
dictRelease((dict*) o->ptr);
break;
case OBJ_ENCODING_ZIPLIST:
zfree(o->ptr);
break;
default:
serverPanic("Unknown hash encoding type");
break;
}
}
void freeModuleObject(robj *o) {
moduleValue *mv = o->ptr;
mv->type->free(mv->value);
zfree(mv);
}
void freeStreamObject(robj *o) {
freeStream(o->ptr);
}
**************************************************************************************
/* This variant of decrRefCount() gets its argument as void, and is useful
* as free method in data structures that expect a 'void free_object(void*)'
* prototype for the free method. */
这个函数是为了满足特定类型函数指针void free_object(void*)调用的需要
void decrRefCountVoid(void *o) {
decrRefCount(o);
}
**************************************************************************************
/* This function set the ref count to zero without freeing the object.
* It is useful in order to pass a new object to functions incrementing
* the ref count of the received object. Example:
*
* functionThatWillIncrementRefCount(resetRefCount(CreateObject(...)));
*
* Otherwise you need to resort to the less elegant pattern:
*
* *obj = createObject(...);
* functionThatWillIncrementRefCount(obj);
* decrRefCount(obj);
*/
这个函数是为了节省步骤
robj *resetRefCount(robj *obj) {
obj->refcount = 0;
return obj;
}
**************************************************************************************
shared.wrongtypeerr = createObject(OBJ_STRING,
sdsnew("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"));
检查类型是否符合,不符合输出错误
int checkType(client *c, robj *o, int type) {
if (o->type != type) {
addReply(c,shared.wrongtypeerr);
return 1;
}
return 0;
}
**************************************************************************************
字符串能否转化成长整形
int isSdsRepresentableAsLongLong(sds s, long long *llval) {
return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR;
}
**************************************************************************************
对象是否能转化为长整形
int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
if (o->encoding == OBJ_ENCODING_INT) {
if (llval) *llval = (long) o->ptr;
return C_OK;
} else {
return isSdsRepresentableAsLongLong(o->ptr,llval);
}
}
**************************************************************************************
/* Optimize the SDS string inside the string object to require little space,
* in case there is more than 10% of free space at the end of the SDS
* string. This happens because SDS strings tend to overallocate to avoid
* wasting too much time in allocations when appending to the string. */
优化字符串对象中SDS字符串的内部存储空间获取空闲的小空间
在SDS字符串末尾超过百分之10的空闲空间时候。发生这种情况的原因在于sds过度分配,
为了避免花费更多时间处理分配额外添加的字符串所需的空间
void trimStringObjectIfNeeded(robj *o) {
if (o->encoding == OBJ_ENCODING_RAW &&
sdsavail(o->ptr) > sdslen(o->ptr)/10)
{
o->ptr = sdsRemoveFreeSpace(o->ptr);
}
}