Redis Source Learning Brief (7) object Principle and Personal Understanding
Object is an encapsulation system in redis.It encapsulates string, list, set, zset and hash as a unified object named robj.This data structure stores data of type, encoding, number of references, data and LRU replacement algorithm.Specifically, first look at the definition of this data structure, which is defined in server.h.
- //redis belongs to the key-value database
- //nosql database, this mapping relationship is maintained using dict
- //The dict entity data (dictentry) has a field void *value
- typedef struct redisObject {
- unsigned type:4;
- /*
- string 0
- list 1
- set 2
- zset 3
- hash 4
- 4 Bits
- */
- unsigned encoding:4;
- //Encoding 4 bit s
- 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 Replacement Algorithm
- int refcount;//Reference Count 32bit 4 bytes
- void *ptr;//32-bit system 4 bytes 64-bit system 8 bytes
- //Point to real data
- } robj;
Let's talk more about this.First, type, defined as follows, is actually four bit s used to store 0-4 data.
- /* 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. */
Then encoding.In general, there are 11 encoding modes, which are also stored in 4 bits, with 4 bits being the maximum of 16 encoding modes.
- #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 */
Next comes lru, which stores the LRU replacement algorithm. For now, let's dig into it and use three bytes of LRU_BITS (24bits) storage.
refcount is a reference pointer, which acts like a smart pointer, calculating the number of references to an object and releasing its usage space when the number of references to an object is 0.
The last void *ptr is the final pointer to the data.These are basically the types of data that were previously analyzed.
The data structure is hard to understand, and then look at its basic implementation function.They are basically implemented in object.c.
First, it is created, which returns a robj pointer based on the type and the data ptr points to.
- robj *createObject(int type, void *ptr) {
- robj *o = zmalloc(sizeof(*o));//Allocate space
- o->type = type;//Setting Type
- o->encoding = OBJ_ENCODING_RAW;//Native encoding mode
- //There are many different types here, and we'll discuss them in more detail later.
- o->ptr = ptr;//Execute real data
- o->refcount = 1;//Reference Count
- /* Set the LRU to the current lruclock (minutes resolution), or
- * alternatively the LFU counter. */
- //Page replacement algorithms are not considered first, but are studied later.
- 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.Make the object shred.Increasing references and decreasing references check this particular reference count.Here's how you can make objects shared by setting the refcout value to int_max.
- robj *makeObjectShared(robj *o) {
- serverAssert(o->refcount == 1);
- o->refcount = OBJ_SHARED_REFCOUNT;
- //Make the object shared and set obj_shared_refcount to int_max
- return o;
- }
Next are two ways to create string objects.
Let's start by introducing how to create native strings.Simply call sdsnewlen's method, then get a string pointer and create it by calling 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) {
- //Create RawStringObject Type
- //Is actually an sds type, and by default encoding is 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));
- }
Another way to create it is to add a string directly behind the object, which is stored directly behind the object.Specifies that the entire object is no more than 64 bytes.
- /* 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) {
- //Step by step through the code
- robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
- //sds headspace allocated robj data and sdshdr8 plus len+1 length, the extra 1 length stores null
- struct sdshdr8 *sh = (void*)(o+1);
- //o+1 grows based on the bytes of robj's structure
- //robj is 16 bytes in total, so plus 16 is just the head of sdshdr8
- o->type = OBJ_STRING;
- o->encoding = OBJ_ENCODING_EMBSTR;
- o->ptr = sh+1;
- //sh+1 is the number of bytes plus sdshdr8, whereas sdshdr8 is 3 bytes len+alloc+flags
- //Then after adding 1, ptr points to sh->buf
- /*
- An interesting phenomenon was found in studying the bytes occupied by characters.
- struct __attribute__ ((__packed__)) sdshdr8 {
- uint8_t len;
- uint8_t alloc;
- unsigned char flags;
- char buf[];
- };
- Because char buf[]] causes the calculation of the size of sdshdr8 to be less than a buf[] odd
- With a pointer, 8 bytes are added for storage
- The length of [] will also be increased if it has a value and will not be calculated if it is not written.The buf's address is the last flags.
- */
- o->refcount = 1;
- //Initialize Reference Count
- if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
- o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
- } else {
- o->lru = LRU_CLOCK();
- }
- //Page algorithms are not studied first.
- //Initialization of sds
- sh->len = len;
- sh->alloc = len;
- sh->flags = SDS_TYPE_8;
- if (ptr == SDS_NOINIT)
- //sds_noint is a static const char *
- //Determine whether to initialize
- sh->buf[len] = '\0';
- else if (ptr) {
- //Initialize to see if the ptr value is NULL
- memcpy(sh->buf,ptr,len);
- sh->buf[len] = '\0';
- } else {
- memset(sh->buf,0,len+1);
- }
- return o;
- }
The encoding of these two strings is invoked through a total string interface.As mentioned above, EmbeddedString consists of the following sections
robj 16-byte sdshdr8 header 3 bytes plus a'0'byte, last stored 44 bytes
3+16+1+44=64.It happens that 64 sections have all the data loaded.
- /* 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) {
- //Ensure that the size of the embedded dedstringbox is 64 or call creteRawStringObject
- //Set the maximum length to 44+16+3+1=64
- //In fact, sdshdr8 can store 256 characters
- if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
- return createEmbeddedStringObject(ptr,len);
- else
- return createRawStringObject(ptr,len);
- }
Finished encapsulating strings.Take a look at the packaging of integers.Overall, when 8 bytes of 32-bit storage can be used, the ptr in robj is the integer directly.Equivalent to the number of instants in the assembly.Beyond that range, the data is stored as a string.The ptr is then used as the pointer.
- robj *createStringObjectFromLongLong(long long value) {
- robj *o;
- if (value >= 0 && value < OBJ_SHARED_INTEGERS) {
- incrRefCount(shared.integers[value]);
- //You probably know that shared is an institution that contains a lot of shared data.
- //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);
- //If you can use 8 bytes of storage, storing it directly in ptr is equivalent to the number of assemblies immediately.
- } else {
- //Use sds's schema storage if you can't store it
- o = createObject(OBJ_STRING,sdsfromlonglong(value));
- }
- }
- return o;
- }
Floating-point numbers are stored in the form of strings.
- /* 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];
- //Human friendly is used to handle floating-point numbers, which you can ignore first
- int len = ld2string(buf,sizeof(buf),value,humanfriendly);
- //Convert a long double to sds and store it
- return createStringObject(buf,len);
- }
Replication of strings between objects is limited to data stored in strings (including integers stored using strings), or integers stored using immediate numbers.
- /* 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;
- }
- }
Following are some other types of creation, most of which are created using the create function of that type and then releasing space, which are relatively simple.There are also functions that reduce the reference count, which are straightforward and simple.Just look straight at it.
- 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);
- }