本文及后续文章,Redis版本均是v3.2.8

我们知道一个database内的这个映射关系是用一个dict来维护的。dict的key固定用一种数据结构来表达,这这数据结构就是动态字符串sds。而value则比较复杂,为了在同一个dict内能够存储不同类型的value,这就需要一个通用的数据结构。针对不同的使用场景,这个通用的数据结构可以使用不同的数据结构实现,这样可以优化在不同场景下的效率。这个通用的数据结构就是robj(redisObject),也是本文主要探讨的redis中的对象是怎么实现的。

robj数据结构

在server.h文件中,定义robj的相关代码

/* A redis object, that is a type able to hold a string / list / set */

/* The actual Redis Object */

#define LRU_BITS 24

#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */

#define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */

typedef struct redisObject {

unsigned type:4;

unsigned encoding:4;

unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */

int refcount;

void *ptr;

} robj;

  • type: 对象的数据类型。占4个bit,可能的取值有5种:OBJ_STRING, OBJ_LIST, OBJ_SET, OBJ_ZSET, OBJ_HASH,分别对应提供给我们长使用的5种数据结构(字符串对象(string)、列表对象(list)、哈希对象(hash)、集合对象(set)和有序集合对象(zset)),稍后再介绍5钟数据类型。

  • encoding: 对象的内部编码方式。占4个bit,可能的取值有10种,稍后再介绍10钟编码方式。

  • lru: lru time,占24个bit。

  • refcount: 引用计数。它允许robj对象在某些情况下被共享。

  • ptr: 数据指针,指向实现对象的底层数据结构。比如,一个代表string的robj,它的ptr可能指向一个sds结构。

对象的类型type

/* Object types */

#define OBJ_STRING 0

#define OBJ_LIST 1

#define OBJ_SET 2

#define OBJ_ZSET 3

#define OBJ_HASH 4

对象的编码encoding

/* Objects encoding. Some kind of objects like Strings and Hashes can be

* internally represented in multiple ways. The 'encoding' field of the object

* is set to one of this fields for this object. */

#define OBJ_ENCODING_RAW 0     /* Raw representation */

#define OBJ_ENCODING_INT 1     /* Encoded as integer */

#define OBJ_ENCODING_HT 2      /* Encoded as hash table */

#define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */

#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */

#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */

#define OBJ_ENCODING_INTSET 6  /* Encoded as intset */

#define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */

#define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */

#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */

我们从以上的注释中知道,对于同一个type,还可能对应不同的encoding,这说明同样的一个数据类型,可能存在不同的内部编码方式。

例如当type = OBJ_STRING时,表示这个robj存储的是一个string,那么encoding可以是下面3种选择:

  • OBJ_ENCODING_RAW: string采用原生的表示方式,即用sds来表示。

  • OBJ_ENCODING_INT: string采用数字的表示方式,实际上是一个long型。

  • OBJ_ENCODING_EMBSTR: string采用一种特殊的嵌入式的sds来表示。

encoding:

  • OBJ_ENCODING_RAW: 最原生的表示方式。其实只有string类型才会用这个encoding值(表示成简单动态字符串sds)。

  • OBJ_ENCODING_INT: 表示成数字。实际用long表示。

  • OBJ_ENCODING_HT: 表示成dict。

  • OBJ_ENCODING_ZIPMAP: 是个旧的表示方式,已不再用。

  • OBJ_ENCODING_LINKEDLIST: 双端列表,已不再用

  • OBJ_ENCODING_ZIPLIST: 表示成ziplist。

  • OBJ_ENCODING_INTSET: 表示成intset。用于set数据结构。

  • OBJ_ENCODING_SKIPLIST: 表示成skiplist。用于sorted set数据结构。

  • OBJ_ENCODING_EMBSTR: 表示成一种特殊的嵌入式的sds。

  • OBJ_ENCODING_QUICKLIST: 表示成quicklist。用于list数据结构。

访问时间lru

lru属性(占24 bit)表示对象最后一次别访问的时间,根据lru判断对象是否应该被释放,本文暂不做分析。

引用计数refcount

由于C语言并不具备内存回收机制,所以redis通过refcount记录robj共享的次数。当refcount为0时即robj对象没有在被应用,表示该robj对象应该被释放,回收内存。

我们看下object.c文件中代码

void incrRefCount(robj *o) {

o->refcount++;

}

void decrRefCount(robj *o) {

if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");

if (o->refcount == 1) {

switch(o->type) {

case OBJ_STRING: freeStringObject(o); break;

case OBJ_LIST: freeListObject(o); break;

case OBJ_SET: freeSetObject(o); break;

case OBJ_ZSET: freeZsetObject(o); break;

case OBJ_HASH: freeHashObject(o); break;

default: serverPanic("Unknown object type"); break;

}

zfree(o);

} else {

o->refcount--;

}

}

从代码可以看出,释放对象时,根据对象的类型type,释放对象保存的数据结构,再释放对象。

例如释放set对象时:

void freeSetObject(robj *o) {

switch (o->encoding) {

case OBJ_ENCODING_HT:

dictRelease((dict*) o->ptr);

break;

case OBJ_ENCODING_INTSET:

zfree(o->ptr);

break;

default:

serverPanic("Unknown set encoding type");

}

}

根据不同的编码实现,调用不同的底层释放函数。

其他对象的操作函数都在object.c文件中,这里就不一一列举了。

在上一篇文章《Redis数据结构之sds》中,我们简单地提到了sds与string的关系,当初我们简单的理解string对象就是sds。

现在我们了解了robj的概念之后,我们在重新总结一下sds与string的关系:

  • string对象在Redis中是用一个robj(redisObject)来表示的。

  • string对象编码方式有3种:OBJ_ENCODING_RAW, OBJ_ENCODING_EMBSTR, OBJ_ENCODING_INT。其中前两种编码使用sds来存储,最后一种OBJ_ENCODING_INT编码直接把string存成了long型。

  • 在对string进行incr, decr等操作的时候,

    如果它内部是OBJ_ENCODING_INT编码,那么可以直接进行加减操作;如果它内部是OBJ_ENCODING_RAW或OBJ_ENCODING_EMBSTR编码,那么Redis会先试图把sds存储的字符串转成long型,如果能转成功,再进行加减操作。

  • 对一个内部表示成long型的string执行append, getrange, setbit等命令,针对的仍然是string的值(即十进制表示的字符串),而不是针对内部表示的long型进行操作。

总结

我们回顾下,robj的作用:

  • 为多种数据类型提供一种统一的表示方式。

  • 允许同一类型的数据采用不同的内部表示,从而在某些情况下尽量节省内存。

  • 支持对象共享和引用计数。当对象被共享的时候,只占用一份内存拷贝,进而节省内存。

--EOF--

Redis数据结构之robj的更多相关文章

  1. Redis数据结构之intset

    本文及后续文章,Redis版本均是v3.2.8 上篇文章<Redis数据结构之robj>,我们说到redis object数据结构,其有5中数据类型:OBJ_STRING,OBJ_LIST ...

  2. Redis数据结构底层知识总结

    Redis数据结构底层总结 本篇文章是基于作者黄建宏写的书Redis设计与实现而做的笔记 数据结构与对象 Redis中数据结构的底层实现包括以下对象: 对象 解释 简单动态字符串 字符串的底层实现 链 ...

  3. Redis 数据结构与内存管理策略(下)

    Redis 数据结构与内存管理策略(下) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  4. Redis 数据结构的实现

    Redis 数据结构的实现 先看个对照关系: Redis数据结构 实现一 实现二 string 整数(如果value能够表示为整数) 字符串 hash 压缩列表(只包含少量键值对, 并且每个键值对的键 ...

  5. Redis数据结构—跳跃表

    目录 Redis数据结构-跳跃表 跳跃表产生的背景 跳跃表的结构 利用跳跃表查询有序链表 Redis跳跃表图示 Redis跳跃表数据结构 小结 Redis数据结构-跳跃表 大家好,我是白泽,最近学校有 ...

  6. redis数据结构附录

    引言 本次对上一次的数据结构知识进行补充,主要有redis数据结构的相关应用场景和内存相关知识 引用计数-内存 redis中的对象回收机制是采用引用计数的方式,首先我们可以通过redis对象结构体代码 ...

  7. 京东云开发者| Redis数据结构(二)-List、Hash、Set及Sorted Set的结构实现

    1 引言 之前介绍了Redis的数据存储及String类型的实现,接下来再来看下List.Hash.Set及Sorted Set的数据结构的实现. 2 List List类型通常被用作异步消息队列.文 ...

  8. Redis 数据结构使用场景

    转自http://get.ftqq.com/523.get 一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的 ...

  9. Redis数据结构

    Redis数据结构 Redis数据结构详解(一)   前言 Redis和Memcached最大的区别,Redis 除啦支持数据持久化之外,还支持更多的数据类型而不仅仅是简单key-value结构的数据 ...

随机推荐

  1. IE 11 flex布局兼容性问题 ---- 不支持min-height 和flex:1

    由于最近项目要嵌入其它平台,所以要做IE11 的兼容,那就用IE11打开网页看一看,一看吓一跳,页脚直接到了页眉的下面,并把主要内容覆盖了,也就是stick footer 布局失效了,我写了一个简易的 ...

  2. git 的简单实用

    一. 安装 Git(git_for_windows.xp510.com.rar) 二. 使用 a) 进入到 git bash(命令行工具) b) 初始化user.name,user.email $ g ...

  3. Flask 模板系统

    模板 基本数据类型 可以执行python语法,如:dict.get(), list['xx'] 比django 更加亲近于 python 传入函数 - django,自动执行 - flask,不自动执 ...

  4. css 溢出overflow

    css 溢出overflow 当一个元素被设置为固定大小,在这个元素中的内容如果超出元素的界限,就会出现溢出的现象. 通常情况下我们可以通过overflow来控制这个属性. overflow语法定义 ...

  5. numpy中的随机数模块

    https://www.cnblogs.com/td15980891505/p/6198036.html numpy.random模块中提供啦大量的随机数相关的函数. 1 numpy中产生随机数的方法 ...

  6. linux usb总线驱动(一)

    目录 linux usb总线驱动框架 USB 介绍 传输类型 控制器接口 2440接口 基本流程 alloc_dev choose_address hub_port_init usb_get_devi ...

  7. vue 开发微信小程序

    介绍 mpvue (github 地址请参见)是一个使用 Vue.js 开发小程序的前端框架.框架基于 Vue.js 核心,mpvue 修改了 Vue.js的 runtime 和 compiler 实 ...

  8. [物理学与PDEs]第3章第4节 磁流体力学方程组的数学结构

    1.  在流体存在粘性.热传导及 $\sigma\neq \infty$ 时, 磁流体力学方程组是一个拟线性对称双曲 - 抛物耦合组. 2.  在流体存在粘性.热传导但 $\sigma=\infty$ ...

  9. [物理学与PDEs]第1章第3节 真空中的 Maxwell 方程组, Lorentz 力 3.2 Lorentz 力

    1. Lorentz 假定, 不论带电体的运动状态如何, 其所受的力密度 (单位体积所受的力) 为 $$\bex {\bf F}=\rho {\bf E}+{\bf j}\times{\bf B} = ...

  10. java Concurrent并发容器类 小结

    Java1.5提供了多种并发容器类来改进同步容器的性能. 同步容器将所有对容器的访问都串行化,以实现他们的线程安全性.这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重减低.  一 ...