工作生活

「Redis源码解读」—数据结构(六)对象

2019-06-30  本文已影响0人  wh4763

知识点

结构体

typedef struct redisObject {  
    // 类型  
    unsigned type:4;          
    // 不使用(对齐位)  
    unsigned notused:2;  
    // 编码方式  
    unsigned encoding:4;  
    // LRU 时间(相对于 server.lruclock)  
    unsigned lru:22;  
    // 引用计数  
    int refcount;  
    // 指向对象的值  
    void *ptr;  
} robj;

字符串对象

字符串对象的编码可以是int、raw或者embstr。
如果一个字符串的内容可以转换为long,那么该字符串就会被转换成为long类型,对象的ptr就会指向该long,并且编码类型也用int类型表示。
普通的字符串有两种,embstr和raw。embstr编码是由redIsObject和sdshdr组成,sdshdr结构体如下

struct sdshdr {
    unsigned int len;
    unsigned int free;
    char buf[];
}

redIsObject占用16字节,如果buf长度是39个字节,那么sdshdr就是8+39+1=48个字节,那么embstr就是64字节,而redis采用的是jemalloc内存分配器,可以分配8,16,32,64字节等大小的内存,而sdshdr最小分配8+8+1=17字节,那么embstr最小就是33字节,需要分配64字节。所以对于redis来说小于等于39字节的字符串采用embstr编码,大于则用raw编码。

列表对象

ziplist是一种压缩链表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储。但当数据量过大时就ziplist就不是那么好用了。因为为了保证他存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。

另一方面,linkedlist编码的列表对象使用双端链表作为底层实现,每个双端链表节点Node都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素

编码转换

当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码
1.列表对象保存的所有字符串元素的长度都小于64字节
2.列表对象保存的元素数量小于512个,不能满足这两个条件的列表对象需要使用linkedlist编码

哈希对象

ziplist中的哈希对象是按照key1,value1,key2,value2这样的顺序存放来存储的。当对象数目不多且内容不大时,这种方式效率是很高的。

编码转换

当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码
1.哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
2.哈希对象保存的键值对数量小于512个,不能满足这两个条件的哈希对象需要使用hashtable编码

集合对象

#define INTSET_ENC_INT16 (sizeof(int16_t))  #define INTSET_ENC_INT32 (sizeof(int32_t))  #define INTSET_ENC_INT64 (sizeof(int64_t))  

intset是一个有序集合,查找元素的复杂度为O(logN),但插入时不一定为O(logN),因为有可能涉及到升级操作。比如当集合里全是int16_t型的整数,这时要插入一个int32_t,那么为了维持集合中数据类型的一致,那么所有的数据都会被转换成int32_t类型,涉及到内存的重新分配,这时插入的复杂度就为O(N)了。是intset不支持降级操作。

编码转换

当集合对象可以同时满足以下两个条件时,集合对象使用intset编码
1.集合对象保存的所有元素都是整数值
2.集合对象保存的元素不超过512,不能满足这两个条件的哈希对象需要使用hashtable编码

有序集合对象

编码转换

1.有序集合保存的元素数量小于128个
2.有序集合保存的所有元素成员的长度都小于64字节
不能满足以上两个条件的有序集合对象将使用skiplist编码

内存回收

redis在自己的对象系统中构建一个引用计数(reference counting)技术实现的内存回收机制,通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。

对象共享

假设键A于键B都创建了一个包含整数100的字符串对象作为值对象时,redis将会通过让键A和键B共享同一个字符串对象节约内存。

对象空转时长

redisObject结构包含的最后一个属性为lru属性,该属性记录了对象最后一次被命令程序访问的时间;
如果redis打开了maxmemory选项,那么当redis占用的内存数超过了maxmemory选项所设置的上限时,空转时长较高的那部分键会被优先被释放,从而回收内存

上一篇 下一篇

猜你喜欢

热点阅读