【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命令。
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对象的更多相关文章
- Redis源码阅读(一)事件机制
Redis源码阅读(一)事件机制 Redis作为一款NoSQL非关系内存数据库,具有很高的读写性能,且原生支持的数据类型丰富,被广泛的作为缓存.分布式数据库.消息队列等应用.此外Redis还有许多高可 ...
- Redis源码阅读(二)高可用设计——复制
Redis源码阅读(二)高可用设计-复制 复制的概念:Redis的复制简单理解就是一个Redis服务器从另一台Redis服务器复制所有的Redis数据库数据,能保持两台Redis服务器的数据库数据一致 ...
- Redis源码阅读(六)集群-故障迁移(下)
Redis源码阅读(六)集群-故障迁移(下) 最近私人的事情比较多,没有抽出时间来整理博客.书接上文,上一篇里总结了Redis故障迁移的几个关键点,以及Redis中故障检测的实现.本篇主要介绍集群检测 ...
- Redis源码阅读(五)集群-故障迁移(上)
Redis源码阅读(五)集群-故障迁移(上) 故障迁移是集群非常重要的功能:直白的说就是在集群中部分节点失效时,能将失效节点负责的键值对迁移到其他节点上,从而保证整个集群系统在部分节点失效后没有丢失数 ...
- Redis源码阅读(四)集群-请求分配
Redis源码阅读(四)集群-请求分配 集群搭建好之后,用户发送的命令请求可以被分配到不同的节点去处理.那Redis对命令请求分配的依据是什么?如果节点数量有变动,命令又是如何重新分配的,重分配的过程 ...
- Redis源码阅读(三)集群-连接初始化
Redis源码阅读(三)集群-连接建立 对于并发请求很高的生产环境,单个Redis满足不了性能要求,通常都会配置Redis集群来提高服务性能.3.0之后的Redis支持了集群模式. Redis官方提供 ...
- Redis源码阅读-Adlist双向链表
Redis源码阅读-链表部分- 链表数据结构在Adlist.h Adlist.c Redis的链表是双向链表,内部定义了一个迭代器. 双向链表的函数主要是链表创建.删除.节点插入.头插入.尾插入. ...
- Redis源码阅读笔记(1)——简单动态字符串sds实现原理
首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...
- [Redis源码阅读]sds字符串实现
初衷 从开始工作就开始使用Redis,也有一段时间了,但都只是停留在使用阶段,没有往更深的角度探索,每次想读源码都止步在阅读书籍上,因为看完书很快又忘了,这次逼自己先读代码.因为个人觉得写作需要阅读文 ...
- Redis源码阅读一:简单动态字符串SDS
源码阅读基于Redis4.0.9 SDS介绍 redis 127.0.0.1:6379> SET dbname redis OK redis 127.0.0.1:6379> GET dbn ...
随机推荐
- hihoCoder 1493 : 歌德巴赫猜想 素数筛法
题意:哥德巴赫猜想认为"每一个大于2的偶数,都能表示成两个质数之和".给定一个大于2的偶数N,你能找到两个质数P和Q满足P<=Q并且P+Q=N吗?如果有多组解,输出P最小的一 ...
- AGC010 - D: Decrementing
原题链接 题意简述 给出一个个数的序列,足够聪明的AB两人轮流进行以下操作: 令一个大于1的数减1,然后所有数除以. 如果一个人不能操作了,那么他就输了. 输入保证所有数都是正整数并且. 分析 这是一 ...
- Enum枚举写的一个简单状态机
今天下雨,心情有点压抑,所以用枚举写个状态机排解一下心情,顺便记录一下枚举使用方法. package statemachine; import java.util.ArrayList; import ...
- asp.net程序发布详解
本文转载自Alan_beijing的博客ASP.NET 程序发布详细过程.内容进行了部分更改. ASP.NET网站的发布,无论是初学者还是高手,在程序的发布过程中或多或少会存在一些问题,譬如VS发布A ...
- Flask Ansible自动化平台搭建(持续更新)
一:简介 使用Ansible + Flask + Celery搭建web平台. 目录结构 . ├── ansible_api │ ├── ansible_playbook_inventory.py ...
- python 下划线--完美解释
Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用'from module import *'导入 __xxx__ 系统定义名字 __xxx 类中的私有变量名 核心风格:避免用下划 ...
- hive: join 遇到问题
在表连接时遇到一个问题: insert overwrite table BF_EVT_CRD_CRT_TRAD2 select BF_EVT_CRD_CRT_TRAD.*, jjkdjk.CUST_N ...
- 【前端】Vue2全家桶案例《看漫画》之六、图片阅读页
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/vue_vux_app_6.html 项目github地址:https://github.com/shamoyuu/ ...
- 关于编译FFMPEG的初级教程
首先我们要下载相关工具,这里不多说,大家按照我的地址去下载文件就好了 MINGW下载地址:http://prdownloads.sourceforge.net/mingw/MinGW-3.1.0-1. ...
- 制作U盘启动CDLinux
用U盘启动CDLinux的好处就此不必多说了,直接上料: 下载必需文件: 1,CDLinuxU盘启动制作工具:CDLinuxU盘启动制作工具 2,CDlinux 0.9.7 集_奶瓶_打气筒_mini ...