Redis 数据结构的底层实现 RealObject

一.realObject

Redis使用 string list zset hash set 五大数据类型来存储键和值。在每次生成一个键值对时,都会生成两个对象,一个储存键一个储存值。redis定义了RealObject结构体表示他们

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
    int refcount;
    void *ptr;
} robj;

1.type

redis 的对象有五种类型,分别是string list zset hash set,type就是用来标识这五种类型的。

/* Object types */
#define OBJ_STRING 0
#define OBJ_LIST 1
#define OBJ_SET 2
#define OBJ_ZSET 3
#define OBJ_HASH 4

(在redis中,键总是一个字符串对象,而值可以是字符串、列表、集合等对象)

2.编码类型encoding

redis的对象的实际编码方式由encoding参数指定,也就是ptr指针指向的数据以何种底层实现存放。

/* 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 -- 简单动态字符串sds*/
#define OBJ_ENCODING_INT 1     /* Encoded as integer -- long类型的整数*/
#define OBJ_ENCODING_HT 2      /* Encoded as hash table -- 字典dict*/
#define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap -- 3.2.5版本后不再使用 */
#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list -- 双向链表*/
#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 -- embstr编码的sds*/
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists -- 由双端链表和压缩列表构成的高速列表*/

可以通过 object encoding key 指令查看值对象的编码

3.访问时间lru

表示该对象最后一次呗访问的时间,占用24bit。保存该值的目的是为了计算对象的空转时长,便于决定是否应该释放该键。

4.引用计数refcount

c语言不具备自动内存回手机制,所以为每个对象设定了引用计数。

  • 当创建一个对象时,记为1;
  • 当被一个新的程序使用时,引用计数++;
  • 不再被一个程序使用时,引用计数--;
  • 当引用计数为0时,释放该对象。回收内存。
void decrRefCount(robj *o) {
    // 引用计数为小于等于0,报错
    if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
    // 引用计数等于1,减1后为0
    // 须要释放整个redis对象
    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;
        }
        // 释放redis对象
        zfree(o);
    } else {
        // 引用计数减1
        o->refcount--;
    }
}

5.ptr指向实际存放的对象

二、OBJ_ENCODING_RAW

RAW编码方式使用简单动态字符串sds来保存字符串对象
struct sdshdr {
    unsigned int len; //buf中已经占用的空间的长度
    unsigned int free;//buf中剩余的可用空间长度
    char buf[];//数据存放位置
};
  • 其中buf[]结束并不依赖于‘\0’,使用len判断结束,可以保存二进制流对象。
  • 预分配,可以减少修改字符串长度增长时造成的再次分配

三、OBJ_ENCODING_EMBSTR

从Redis 3.0 版本开始,字符串引入了embstr编码方式,长度小于OBJ_ENCONDING_EMBSTR_SIZE_LIMIT的字符串将以EMBSTR方式存储。

EMBSTR方式的意思是 embedded string,字符串的空间将会和redisObject对象的空间一起分配,两者分配在同一个内存块中。

redis中内存分配使用的是jemalloc,jemalloc分配内存的时候是按照8,16,32,64作为块的单位进行飞扑的。为了保证采用这种编码方式的字符串能被jemalloc分配在同一个块(chunk)中,该块长度不能超过64,故字符串长度限制OBJ_ENCONDING_EMBSTR_SIZE_LIMIT = 64 - sizeof('\0') -sizeof(robj) -sizeof(sdshdr) =39.

这样可以有效减少内存分配的次数,提高内存分配的效率,降低碎片率。

结构同样适用 sdshdr(见上文)

四、OBJ_ENCODING_HT (hashtable)

猜你喜欢

转载自www.cnblogs.com/chafanbusi/p/10711819.html