结构定义

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

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

结构定义中的type:4encoding: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命令。

hget命令实现代码:

void hgetCommand(client *c) {
robj *o; // key不存在,返回空
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,o,OBJ_HASH)) return; addHashFieldToReply(c, o, c->argv[2]->ptr);
}

hget命令的实现最终是调用addHashFieldToReply函数(代码如下),该函数是通过判断哈希对象的编码来决定使用什么函数来获取哈希对象具体field的值,其他命令的实现也是大同小异。

static void addHashFieldToReply(client *c, robj *o, sds field) {
int ret; if (o == NULL) {
addReply(c, shared.nullbulk);
return;
} // 根据底层不同编码获取field的值
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX; ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
if (ret < 0) {
addReply(c, shared.nullbulk);
} else {
if (vstr) {
addReplyBulkCBuffer(c, vstr, vlen);
} else {
addReplyBulkLongLong(c, vll);
}
} } else if (o->encoding == OBJ_ENCODING_HT) {
sds value = hashTypeGetFromHashTable(o, field);
if (value == NULL)
addReply(c, shared.nullbulk);
else
addReplyBulkCBuffer(c, value, sdslen(value));
} else {
serverPanic("Unknown hash encoding");
}
}

总结

redis中的很多操作都是基于上面介绍的redis对象,了解这些对象的底层实现,可以为之后更多的redis特性做准备。

【redis源码阅读】redis对象的更多相关文章

  1. Redis源码阅读(一)事件机制

    Redis源码阅读(一)事件机制 Redis作为一款NoSQL非关系内存数据库,具有很高的读写性能,且原生支持的数据类型丰富,被广泛的作为缓存.分布式数据库.消息队列等应用.此外Redis还有许多高可 ...

  2. Redis源码阅读(二)高可用设计——复制

    Redis源码阅读(二)高可用设计-复制 复制的概念:Redis的复制简单理解就是一个Redis服务器从另一台Redis服务器复制所有的Redis数据库数据,能保持两台Redis服务器的数据库数据一致 ...

  3. Redis源码阅读(六)集群-故障迁移(下)

    Redis源码阅读(六)集群-故障迁移(下) 最近私人的事情比较多,没有抽出时间来整理博客.书接上文,上一篇里总结了Redis故障迁移的几个关键点,以及Redis中故障检测的实现.本篇主要介绍集群检测 ...

  4. Redis源码阅读(五)集群-故障迁移(上)

    Redis源码阅读(五)集群-故障迁移(上) 故障迁移是集群非常重要的功能:直白的说就是在集群中部分节点失效时,能将失效节点负责的键值对迁移到其他节点上,从而保证整个集群系统在部分节点失效后没有丢失数 ...

  5. Redis源码阅读(四)集群-请求分配

    Redis源码阅读(四)集群-请求分配 集群搭建好之后,用户发送的命令请求可以被分配到不同的节点去处理.那Redis对命令请求分配的依据是什么?如果节点数量有变动,命令又是如何重新分配的,重分配的过程 ...

  6. Redis源码阅读(三)集群-连接初始化

    Redis源码阅读(三)集群-连接建立 对于并发请求很高的生产环境,单个Redis满足不了性能要求,通常都会配置Redis集群来提高服务性能.3.0之后的Redis支持了集群模式. Redis官方提供 ...

  7. Redis源码阅读-Adlist双向链表

    Redis源码阅读-链表部分- 链表数据结构在Adlist.h   Adlist.c Redis的链表是双向链表,内部定义了一个迭代器. 双向链表的函数主要是链表创建.删除.节点插入.头插入.尾插入. ...

  8. Redis源码阅读笔记(1)——简单动态字符串sds实现原理

    首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...

  9. [Redis源码阅读]sds字符串实现

    初衷 从开始工作就开始使用Redis,也有一段时间了,但都只是停留在使用阶段,没有往更深的角度探索,每次想读源码都止步在阅读书籍上,因为看完书很快又忘了,这次逼自己先读代码.因为个人觉得写作需要阅读文 ...

  10. Redis源码阅读一:简单动态字符串SDS

    源码阅读基于Redis4.0.9 SDS介绍 redis 127.0.0.1:6379> SET dbname redis OK redis 127.0.0.1:6379> GET dbn ...

随机推荐

  1. SmileyFace——基于OpenCV的人脸人眼检测、面部识别程序

    项目地址 https://github.com/guoyaohua/SmileyFace 开发环境 Visual Studio 2010 MFC + OpenCV 功能描述 静态图像人脸检测 视频人脸 ...

  2. yum仓库详细解读

    Yum:Yellowdog Updater,Modified的简称,起初由yellow dog发行版的开发者Terra Soft研发,用Python编写,后经杜克大学的Linux@Duke开发团队进行 ...

  3. HashMap/HashSet,hashCode,哈希表

    hash code.equals和“==”三者的关系 1) 对象相等则hashCode一定相等: 2) hashCode相等对象未必相等. == 是比较地址是否相等,JAVA中声明变量都是引用嘛,不同 ...

  4. 分布式mysql中间件(mycat)

    1.   MyCAT概述 1.1 背景 随着传统的数据库技术日趋成熟.计算机网络技术的飞速发展和应用范围的扩充,数据库应用已经普遍建立于计算机网络之上.这时集中式数据库系统表现出它的不足: (1)集中 ...

  5. 4.2 PCIe体系结构的组成部件

    PCIe总线作为处理器系统的局部总线,其作用与PCI总线类似,主要目的是为了连接处理器系统中的外部设备,当然PCIe总线也可以连接其他处理器系统.在不同的处理器系统中,PCIe体系结构的实现方法略有不 ...

  6. Ubutu Chrome 出现adobe flash is out of date的解决方法

    我们需要到官网下载flash player,网址:https://get.adobe.com/flashplayer/ 不过这里要说明一下: 一般的浏览器使用的是npapi,即adobe flash ...

  7. 芝麻HTTP:批量部署Splash负载集群

    安装Ansible: 看官方文档去:http://www.ansible.com.cn/index.html 好像这个主控端不支持Windows? 大家虚拟机装个Ubuntu吧. 闲话少扯直接上干货: ...

  8. String.valueOf(Thread.currentThread().getContextClassLoader().getResource("")) 获取项目的绝对路径(shiro项目中来的八)

    一,上代码 String.valueOf(Thread.currentThread().getContextClassLoader().getResource("")) file: ...

  9. HALCON不支持的设备中,获取图像

    HALCON不支持的设备中,获取图像   参考(HALCON手册): Solution_guide_II_A_image_acquisiton.pdf image_acquisition_interf ...

  10. Angular通过订阅观察者对象实现不同组件中数据的实时传递

    在angular官方定义中,组件直接的数据交换只要在父子直接传递,但是我们在项目中经常需要在各种层级之间传递数据,下面介绍关于订阅可观察对象实现的数据传递. 首先定义一个服务app.sevice.ts ...