redis对象机制

从哪里开始学习底层?

我们在前文已经阐述了Redis 5种基础数据类型详解和Redis入门 - 数据结构:Stream详解;那么这些基础类型的底层是如何实现的呢?
带着这个问题我们来着手理解底层设计,首先看下图:
在这里插入图片描述
它反映了Redis的每种对象其实都由对象结构(redisObject) 与 对应编码的数据结构组合而成,而每种对象类型对应若干编码方式,不同的编码方式所对应的底层数据结构是不同的。
所以,我们需要从几个个角度来着手底层研究:
对象设计机制: 对象结构(redisObject)
编码类型和底层数据结构: 对应编码的数据结构

为什么Redis设计redisObject对象

1.在redis的命令中,用于对键进行处理的命令占了很大一部分,而对于键所保存的值的类型(键的类型),键能执行的命令又各不相同。如: LPUSH 和 LLEN 只能用于列表键, 而 SADD 和 SRANDMEMBER 只能用于集合键, 等等; 另外一些命令, 比如 DEL、 TTL 和 TYPE, 可以用于任何类型的键但是要正确实现这些命令, 必须为不同类型的键设置不同的处理方式: 比如说, 删除一个列表键和删除一个字符串键的操作过程就不太一样。
2.以上的描述说明, Redis 必须让每个键都带有类型信息, 使得程序可以检查键的类型, 并为它选择合适的处理方式.
3.比如说, 集合类型就可以由字典和整数集合两种不同的数据结构实现, 但是, 当用户执行 ZADD 命令时, 他/她应该不必关心集合使用的是什么编码,只要 Redis 能按照 ZADD 命令的指示, 将新元素添加到集合就可以了。
4.这说明, 操作数据类型的命令除了要对键的类型进行检查之外, 还需要根据数据类型的不同编码进行多态处理
5.为了解决以上问题, Redis 构建了自己的类型系统, 这个系统的主要功能包括:

redisObject对象的主要功能

1.基于redisObject对象的类型检查
2.基于redisObject对象的显式多态函数
3.对 redisObject 进行分配、共享和销毁的机制

redisObject对象的数据结构

/*
 * Redis 对象
 */
typedef struct redisObject {

    // 类型
    unsigned type:4;

    // 编码方式
    unsigned encoding:4;

    // LRU - 24位, 记录最末一次访问时间(相对于lru_clock); 或者 LFU(最少使用的数据:8位频率,16位访问时间)
    unsigned lru:LRU_BITS; // LRU_BITS: 24

    // 引用计数
    int refcount;

    // 指向底层数据结构实例
    void *ptr;

} robj;

type

记录了对象所保存的值的类型

/*
* 对象类型
*/
#define OBJ_STRING 0 // 字符串
#define OBJ_LIST 1 // 列表
#define OBJ_SET 2 // 集合
#define OBJ_ZSET 3 // 有序集
#define OBJ_HASH 4 // 哈希表

encoding

记录了对象所保存的值的编码

/*
* 对象编码
*/
#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  /* 注意:版本2.6后不再使用. */
#define OBJ_ENCODING_LINKEDLIST 4 /* 注意:不再使用了,旧版本2.x中String的底层之一. */
#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 */
#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */

ptr

ptr是一个指针,指向实际保存值的数据结构,这个数据结构由type和encoding属性决定。举个例子, 如果一个redisObject 的type 属性为OBJ_LIST , encoding 属性为OBJ_ENCODING_QUICKLIST ,那么这个对象就是一个Redis 列表(List),它的值保存在一个QuickList的数据结构内,而ptr 指针就指向quicklist的对象;

lru

记录了对象最后一次被命令程序访问的时间

Redis如何处理一条命令

1.根据给定的key,在数据库字典中查找和他相对应的redisObject,如果没找到,就返回NULL;
2.检查redisObject的type属性和执行命令所需的类型是否相符,如果不相符,返回类型错误;
3.根据redisObject的encoding属性所指定的编码,选择合适的操作函数来处理底层的数据结构;
4.返回数据结构的操作结果作为命令的返回值。
比如执行LPOP命令
在这里插入图片描述

对象共享

1.redis一般会把一些常见的值放到一个共享对象中,这样可使程序避免了重复分配的麻烦,也节约了一些CPU时间。
2.共享对象只能被字典和双向链表这类能带有指针的数据结构使用。像整数集合和压缩列表这些只能保存字符串、整数等自勉之的内存数据结构

引用计数以及对象的销毁

redisObject中有refcount属性,是对象的引用计数,显然计数0那么就是可以回收。
1.每个redisObject结构都带有一个refcount属性,指示这个对象被引用了多少次;
2.当新创建一个对象时,它的refcount属性被设置为1;
3.当对一个对象进行共享时,redis将这个对象的refcount加一;
4.当使用完一个对象后,或者消除对一个对象的引用之后,程序将对象的refcount减一;
5.当对象的refcount降至0 时,这个RedisObject结构,以及它引用的数据结构的内存都会被释放。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值