结构定义

在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. HDU - 1043 A* + 康托 [kuangbin带你飞]专题二

    这题我第一次用的bfs + ELFhash,直接TLE,又换成bfs + 康托还是TLE,5000ms都过不了!!我一直调试,还是TLE,我才发觉应该是方法的问题. 今天早上起床怒学了一波A*算法,因 ...

  2. Spring cloud oauth2.0 access_token 永不失效设置方法

    在AuthorizationServerConfigurerAdapter,重写一个TokenServices,注意这里的@Primary 非常重要,否则会有3个同类型的Bean,无法注入,会抛出以下 ...

  3. Hadoop 错误归档库

    在hive中操作任意mapreduce相关语句 The size of Container logs revealed the below error: 2015-04-24 11:41:41,858 ...

  4. R语言︱文件读入、读出一些方法罗列(批量xlsx文件、数据库、文本txt、文件夹)

    笔者寄语:小规模的读取数据的方法较为简单并且多样,但是,批量读取目前看到有以下几种方法:xlsx包.RODBC包.批量转化成csv后读入. R语言中还有一些其他较为普遍的读入,比如代码包,R文件,工作 ...

  5. FusionWidgets Bulb图

    1.数据源提供 Bulb.xml: <?xml version="1.0" encoding="UTF-8"?> <chart lowerLi ...

  6. systemd 编写服务管理脚本

    我们运行 linux 服务器的主要目的是通过运行程序提供服务,比如 mysql.web server等.因此管理 linux 服务器主要工作就是配置并管理上面运行的各种服务程序.在 linux 系统中 ...

  7. Tornado模块

    Tornado 一个轻量级的Web框架 简介 1.Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个拥有非常高性能的框架.此外,它还拥有处理安全性.用户验证.社 ...

  8. Intel_CS_WebRTC 验证性测试

      机器: Centos 7.2 一.配置阿里云源 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.163.com/.help/Cen ...

  9. java 堆 栈 常量池

    java 堆中保存new 出来的对象(每个对象都包含一个与之对应的class的信息,[class信息存放在方法区]),堆中分配的内存,有虚拟机的自动垃圾回收器管理,栈内存只对其所属线程可见. java ...

  10. 常用UI模板,loading框,提醒框,弹框确认框

    css部分 #public_box{width:100%;height:100%;position:fixed;top:0;left:0;z-index:100;background:rgba(0,0 ...