【redis源码阅读】redis对象

redis中,对象的数据结构定义如下:

​typedef struct redisObject { ​unsigned type:4; ​unsgined encoding:4; ​unsigned lru:LRU_BITS; ​int refcount; ​void *ptr; ​}

结构定义中的type:4、encoding:4这种定义方式称为位段类型

使用位段类型的好处就是避免浪费内存,如果使用unsigned int type定义type字段,需要4个字节,而使用unsigned type:4,只需要4个位段就足够了。

参数说明

redis对象有许多特性,比如:类型检查(通过type实现)、命令多态(encoding实现)、内存共享(通过refcount实现)等等,这些特性都是通过redisObject中的参数实现的。

type

对象类型,它的取值范围有五个,分别是redis使用的五种对象类型:

#define OBJ_STRING 0

#define OBJ_LIST 1

​#define OBJ_SET 2

#define OBJ_ZSET 3

#define OBJ_HASH 4

在执行命令前对type字段进行检查,可判断出对象是否是命令允许执行的对象类型。

encoding

对象使用的编码类型,它的取值范围有下面这些:

#define OBJ_ENCODING_RAW 0 /* 动态字符串 */

#define OBJ_ENCODING_INT 1 /* 整数 */

#define OBJ_ENCODING_HT 2 /* 哈希表 */

#define OBJ_ENCODING_ZIPMAP 3

#define OBJ_ENCODING_LINKEDLIST 4 /* 旧的列表编码,现在不再使用了 */

#define OBJ_ENCODING_ZIPLIST 5 /* 压缩表 */

#define OBJ_ENCODING_INTSET 6 /* 整数集合 */

#define OBJ_ENCODING_SKIPLIST 7 /* 跳跃表 */

#define OBJ_ENCODING_EMBSTR 8 /* 用于保存短字符串的编码类型 */

#define OBJ_ENCODING_QUICKLIST 9 /* 压缩链表和双向链表组成的快速列表 */

在调用命令的时候,redis还会根据对象使用的编码类型来选择正确的底层对象,执行对应函数的实现代码。

lru

最近最后一次被命令访问的时间 或者 最近最少使用的数据。

在执行OBJECT IDLETIME命令时,通过当前时间减去lru属性的值,得到键的空转时长。另外,如果服务器打开了maxmemory选项,且使用的内存回收算法是volatile-lur或者allkeys-lru,那么当服务器占用的内存超过了maxmemory的上限值,空转时长较高的键会优先被服务器释放,从而回收内存。

refcount

对象的引用计数。

redis的对象共享和内存回收特性就是通过refcount属性来实现,通过将refcount + 1实现对象共享;进行内存回收检查时,检查refcount == 0的对象,将对象进行回收。

ptr

指向底层数据结构用于保存数据的指针。

对象使用的数据结构

redis有五种对象,不同对象可能用到的数据结构如下图所示:

编码转换与命令多态

同一种对象使用不同的数据结构是通过encoding来实现,而且,同一个命令的实现方法会根据对象的编码属性而变化,这是命令的多态实现。
以哈希对象为例看看编码转换以及命令多态等特性是怎么实现的。

哈希对象

哈希对象使用的编码有:ziplist、hashtable。

如果使用压缩表作为底层实现,每当有新的键值对需要加入哈希对象,会先添加键节点到链表,然后添加值节点。

使用hashtable作为底层实现,每一个新的键值对都会使用字典键值对来保存,键和值分别是字符串对象。

使用不同结构保存后的结构图如下所示:

ziplist编码

hashtable编码

编码转换

每一种对象在使用编码的时候都有一定的条件,使用ziplist编码的哈希对象都应该满足两个条件:

1、所有键值对的键和值字符串对象长度小于64字节

2、哈希对象保存的键值对数量小于512个

如果不能满足上述条件时,redis会进行对哈希对象底层数据结构进行从压缩表到字典的转换,实现步骤是遍历压缩表,获取压缩表中的键和值,使用得到的键和值创建一个字典对象,然后添加字典里,具体代码如下:

hashTypeIterator *hi; dict *dict; int ret; // 创建遍历器对象和哈希表 hi = hashTypeInitIterator(o); dict = dictCreate(&hashDictType, NULL); while (hashTypeNext(hi) != C_ERR) { sds key, value; // 用获取ziplis中的key、value新增键值对到哈希表 key = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY); value = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE); ret = dictAdd(dict, key, value); if (ret != DICT_OK) { serverLogHexDump(LL_WARNING,"ziplist with dup elements dump", o->ptr,ziplistBlobLen(o->ptr)); serverPanic("Ziplist corruption detected"); } } hashTypeReleaseIterator(hi); zfree(o->ptr); o->encoding = OBJ_ENCODING_HT; o->ptr = dict; 命令多态

命令多态是检查对象的编码,然后执行不同的实现方式。比如哈希对象中的hget命令。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxgwd.html