Redis源码分析(八)——数据类型 redisObject

前面分析了Redis底层的几种基本数据结构,但是这些数据结构在redis中并不是直接使用的,而是封装成redis的数据类型(redisObject)来提供给系统使用。

Redis的键值对可以保存不同类型的值,因此需要对键值的类型进行检查并根据不同类型进行多态处理。 为了基于类型的操作更加方便,Redis创建了自己的类型系统,主要包括:字符串、哈希表、列表、集合 和有序集五种类型,这些类型都是基于对前面的基本数据结构的封装实现(Redis内部称为编码,encoding)的,且同一种数据类型可能有多种底层实现。 因此在对redis的数据进行操作时需要检查数据类型,并且根据其不同的底层实现执行不同的多态处理。这也就要求Redis的每个键都带有类型信息,是的程序可以检查键的类型,并为它选择合适的处理方式。


为了解决以上问题,Redis创建了自己的类型系统,这个系统的主要功能包括:

* redisObject对象

*基于redisObject对象的类型检查

*基于redisObject对象的显示多态函数

*对redisObject进行分配,共享和销毁的机制

redisObject是Redis类型系统的核心,数据库中的每个键、值、以及Redis本身处理的参数。都表示为这种数据类型。

Note:C并不是面向对象语言,这里使用将redisObject称为对象是为了讲述的方便,同时通过OOP术语,也便于理解,redisObject实际上只是一个结构类型。


redisObject定义在redis.h中:

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

/* The actual Redis Object */
//redis对象包括了五种类型:字符串  列表  集合 有序集  哈希表,而每种类型
//有自己的底层实现的数据结构(即编码方式)(一种类型可能有多种底层实现方式)
#define REDIS_LRU_BITS 24
#define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */
#define REDIS_LRU_CLOCK_RESOLUTION 1 /* LRU clock resolution in seconds */
typedef struct redisObject {
    unsigned type:4;//类型
    unsigned encoding:4;//编码方式
    unsigned lru:REDIS_LRU_BITS;//LRU时间(相对于server.lrulock) /* lru time (relative to server.lruclock) */
    int refcount;//引用计数
    void *ptr;//指向对象的值
} robj;
注:redisObject结构的ptr指向实际保存值的数据结构,这个数据结构由type属性和encoding属性决定。 例如:   如果一个 redisObjec的type属性为REDIS_LIST,encoding属性为REDIS_ENCODING_LINKEDLIST,那么这个对象的类型就是一个Redis列表,他保存的值在一个双链表内,而ptr指针就指向这个双端链表。

<span style="background-color: rgb(51, 204, 255);"></span>
/* Object types */
//对象类型
#define REDIS_STRING 0  //字符串
#define REDIS_LIST 1    //列表
#define REDIS_SET 2      //集合
#define REDIS_ZSET 3          //有序集
#define REDIS_HASH 4         //哈希表

/* 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. */
 //enconding记录了对象所保存值的编码
#define REDIS_ENCODING_RAW 0   //编码为字符串  /* Raw representation */
#define REDIS_ENCODING_INT 1     //编码为整数 /* Encoded as integer */
#define REDIS_ENCODING_HT 2      //编码为zipmap /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4  //编码为双端链表/* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5      //编码为压缩列表/* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6      //编码为整数集合 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7     //编码为跳跃表/* Encoded as skiplist */


RedisObject、Redis的所有数据类型 以及Redis类型所有的编码方式(底层实现)三者之间的关系如下图



Note:REDIS_ENCODING_ZIPMAP从redis2.6开始,已经不再是任何数据类型的底层结构。


命令的类型检查和多态

当执行一个处理数据类型的命令时,Redis执行以下步骤:

1、根据给定的key,在数据库字典中查找和它对应的redisObject,如果没有找到则返回NULL

2、检查redisObject的type的属性和执行命令所需的类型是否相符,不符,则返回类型错误

3、根据redisObject的encoding属性所指定的编码,选择合适的操作函数来处理底层数据结构

4、返回数据结构的操作结果作为返回值。

例:一个对key执行LPOP命令的完整过程如下:


对象共享:

一些对象在Redis中非常常见,如命令返回值:OK  ERROR WRONGTYPE等字符,以及一些小范围的整数。对于这些常见对象,Redis在内部使用了Flyweight模式: 预分配一些常见值的对象,程序避免重复分配麻烦,也节约了一些CPU时间。

Redis预分配的值对象有:

* 各种命令的返回值:OK ERROR WRONGTYPE 等。

*包括0在内的,小于redis.h/REDIS_SHARED_INTEGERS(10000)的所有整数值。

因为命令的反复值都是直接返回给客户端,其值不需要共享; 而对于较小的整数值,如果某命令的输入值是一个小于REDIS_SHARED_INTEGERS的整数对象,那么当这个对象被保存进数据库时,Redis就会释放原来的值,并将值的指针指向共享对象。

例如: 三个列表,他们都带有指向共享对象数组中某个值对象的指向:


则三个列表的值分别为:

列表A:[20120101,300,10086]

列表B:[81,1234567890,999]

列表C:[100,0,-25,123]

需要注意的是:共享对象只能被带指针的数据结构使用。 如字典,双端链表都带有指针,而像整数集合和压缩列表这些只能保存字符串、整数等字面值的内存数据结构就不能使用共享对象。



引用计数及对象的销毁:

当将redisObject用作数据库的键或值而不是作为存储参数时,其生命周期是非常长的,而C语言本身没有释放内存的机制,如果只依靠程序员的记忆来对对象进行追踪和销毁基本是不可能的。 另外,对于共享对象,可能被多次引用,因此关于被引用了多少次也是需要考虑的问题。

Redis对象系统使用了引用计数来负责维持和销毁对象:

 每个对象结构都有一个ref引用计数,当计数递减为0时,这个redisObject结构以及它所引用的底层数据结构的内存都会被释放。




小结:

*Redis使用自己的实现的对象机制来实现类型判断、命令多态和基于引用计数的垃圾回收

*一种Redis类型可以有多种底层实现

* Redis会预分配一些常用的数据对象,并通过共享这些对象来减少内存占用,和避免频繁的为小对象分配内存。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值