本文主要是介绍Redis源码学习简记(七)object原理与个人理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Redis源码学习简记(七)object原理与个人理解
object是redis中的封装系统。其把string,list,set,zset与hash封装成一个统一的对象,命名为robj。该数据结构中,存储了类型,编码,引用次数,数据与LRU替换算法的一些数据。具体先看看这个数据结构的定义,在server.h中定义。
- //redis属于key-value 数据库
- //nosql数据库,这种映射关系使用dict用来维护
- //而dict实体数据(dictentry)中有字段void *value
- typedef struct redisObject {
- unsigned type:4;
- /*
- string 0
- list 1
- set 2
- zset 3
- hash 4
- 4个bit
- */
- unsigned encoding:4;
- //编码方式 4个bit
- 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). */
- //24bit,LRU替换算法
- int refcount;//引用计数32bit 4字节
- void *ptr;//32位系统4字节 64位系统8字节
- //指向真正数据
- } robj;
来详细讲一下这里面的东西。首先type,定义如下,其实就是4个bit用来存储0~4的数据。
- /* 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. */
然后编码方式encoding。编码方式的话,总的来说有11种,也是用4个bit来存,4个bit最大为16种编码。
- #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 */
接下来的为lru,用于存储LRU替换算法,现在先不深究,使用了LRU_BITS (24bits)三个字节存储。
refcount为引用指针,作用跟智能指针相似,用于计算对象的引用次数,当对象引用次数为0时,则释放其使用空间。
最后的void *ptr则为最终指向数据的指针。这些数据基本就是之前分析的那些类型的数据。
数据结构不难理解,然后看看他的基本实现函数吧。基本都在object.c中实现。
首先是创建,该函数根据类型与ptr指向的数据返回一个robj指针。
- 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), or
- * alternatively the LFU counter. */
- //对于页面置换算法先不考虑,后面研究到了再看。
- if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
- o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
- } else {
- o->lru = LRU_CLOCK();
- }
- return o;
- }
设置一种特殊的refcount。使得该对象时shred。增加引用于减少引用,都会检查这种特殊的引用计数。下面是使得对象变为共享的方法,其实就是将refcout数值设为int_max.
- robj *makeObjectShared(robj *o) {
- serverAssert(o->refcount == 1);
- o->refcount = OBJ_SHARED_REFCOUNT;
- //使得对象为共享,设置为obj_shared_refcount 其大小为int_max
- return o;
- }
接下来是创建字符串对象的两种方法。
先来介绍原生字符串的创建方法。简单的调用sdsnewlen的方法,然后得到字符串指针后在调用create进行创建操作。
- /* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
- * string object where o->ptr points to a proper sds string. */
- robj *createRawStringObject(const char *ptr, size_t len) {
- //创建RawStringObject类型
- //其实是一个sds类型,而在默认情况下,encoding为OBJ_ENCODING_RAW
- /*
- #define OBJ_STRING 0
- #define OBJ_LIST 1
- #define OBJ_SET 2
- #define OBJ_ZSET 3
- #define OBJ_HASH 4
- */
- return createObject(OBJ_STRING, sdsnewlen(ptr,len));
- }
另外一种创建的方法这是一种字符串直接是添加在对象后面,该字符串直接存储在对象的后面。规定整个对象不大于64个字节。
- /* 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. */
- robj *createEmbeddedStringObject(const char *ptr, size_t len) {
- //先一步步看代码
- robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
- //分配了robj数据与sdshdr8的sds头空间加上了len+1的长度,多出来的1长度存储null
- struct sdshdr8 *sh = (void*)(o+1);
- //o+1根据robj的结构体的字节进行增长
- //robj总共16字节,那么加上16刚刚好是sdshdr8的头部
- o->type = OBJ_STRING;
- o->encoding = OBJ_ENCODING_EMBSTR;
- o->ptr = sh+1;
- //sh+1即加上sdshdr8的字节数,而sdshdr8为3字节即len+alloc+flags
- //那么加1后,ptr指向的为sh->buf
- /*
- 在研究字符所占的字节数中发现一个有趣的现象;
- struct __attribute__ ((__packed__)) sdshdr8 {
- uint8_t len;
- uint8_t alloc;
- unsigned char flags;
- char buf[];
- };
- 由于char buf[] 会导致计算sdshdr8的大小不算上buf[]很奇怪
- 而使用指针的话,则会加上8字节用于存储
- 而[]中有值时也会增加其长度,若不写值时则会不计算。而buf的地址为flags最后一位。
- */
- o->refcount = 1;
- //初始化引用计数
- if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
- o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
- } else {
- o->lru = LRU_CLOCK();
- }
- //对于页面算法先不研究。
- //sds的初始化
- sh->len = len;
- sh->alloc = len;
- sh->flags = SDS_TYPE_8;
- if (ptr == SDS_NOINIT)
- //sds_noint 为一个静态const char *
- //判断是否要初始化
- sh->buf[len] = '\0';
- else if (ptr) {
- //初始化的话则看ptr的值是否为NULL
- memcpy(sh->buf,ptr,len);
- sh->buf[len] = '\0';
- } else {
- memset(sh->buf,0,len+1);
- }
- return o;
- }
这两种字符串的编码,通过一个总的字符串接口进行调用。这里面如上面所说的,EmbeddedString总共包括下面几个部分
robj 16字节 sdshdr8头部3个字节 加上一个 '\0' 一个字节,最后存储内容是44个字节
3+16+1+44=64。刚好是64节装完所有的数据。
- /* Create a string object with EMBSTR encoding if it is smaller than
- * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
- * used.
- *
- * 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. */
- #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
- robj *createStringObject(const char *ptr, size_t len) {
- //保证使用embeddedstringboject的大小为64否则调用creteRawStringObject
- //设置其长度最长为44+16+3+1=64
- //事实上sdshdr8可存储256个字符
- if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
- return createEmbeddedStringObject(ptr,len);
- else
- return createRawStringObject(ptr,len);
- }
看完了字符串的封装。观察一下整型的封装方法。总的来说当能使用8个字节32位存储的时候,robj中的ptr直接就是该整数。相当于汇编中的立即数。若超出该范围,那么就是用字符串的形式存储该数据。这时ptr就当指针使用了。
- robj *createStringObjectFromLongLong(long long value) {
- robj *o;
- if (value >= 0 && value < OBJ_SHARED_INTEGERS) {
- incrRefCount(shared.integers[value]);
- //大概了解了shared 它是一个机构体,包含了大量共享的的数据。
- //struct sharedObjectsStruct
- 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);
- //若能使用8字节存储,直接将其存储到ptr中相当于汇编的立即数。
- } else {
- //存不了则使用sds的模式存储
- o = createObject(OBJ_STRING,sdsfromlonglong(value));
- }
- }
- return o;
- }
存储浮点数的方式,都是转化为字符串的形式。
- /* Create a string object from a long double. If humanfriendly is non-zero
- * it does not use exponential format and trims trailing zeroes at the end,
- * however this results in loss of precision. Otherwise exp format is used
- * and the output of snprintf() is not modified.
- *
- * The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
- robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
- char buf[MAX_LONG_DOUBLE_CHARS];
- //humanfriendly用于处理浮点数,可以先不理会
- int len = ld2string(buf,sizeof(buf),value,humanfriendly);
- //将long double转换成sds,然后进行存储
- return createStringObject(buf,len);
- }
对象间字符串的复制,仅限于字符串存储的数据(这里包括了使用字符串存储的整型),或者使用立即数存储的整型。
- /* 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 integere 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. */
- 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;
- }
- }
接下来就是一些别的类型的创建,大多数都是使用该类型的create函数进行创建,后又释放空间的函数,都是比较简单。还有引用计数减少的函数,都比较直接简单。直接看看就好了。
- 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);
- }
- 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);
- }
- void incrRefCount(robj *o) {
- if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++;
- }
- 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");
- if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;
- }
- }
- /* 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 decrRefCountVoid(void *o) {
- decrRefCount(o);
- }
这篇关于Redis源码学习简记(七)object原理与个人理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!