Redis设计与实现(一)对象

Redis对象系统中包含字符串对象、列表对象、哈希对象、集合对象、有序集合对象

实现了基于引用计数的内存回收机制。

一,对象的类型与编码

Redis使用对象来表示数据库中的键和值。

/*
 * Redis 对象
 */ 
typedef struct redisObject { 
    // 类型 
    unsigned type:4;           
    // 编码方式 
    unsigned encoding:4; 
    // LRU 时间(相对于 server.lruclock) 
    unsigned lru:22; 
    // 引用计数 
    int refcount; 
    // 指向底层实现数据结构的指针
    void *ptr; 
} robj;

type表示了该对象的对象类型:

  • REDIS_STRING 字符串对象
  • REDIS_LIST 列表对象
  • REDIS_HASH 哈希对象
  • REDIS_SET 集合对象
  • REDIS_ZSET 有序集合对象

举例:

(1)字符串对象

字符串对象的编码可以是int、raw、embstr

如果字符串对象保存的是整数值,并且这个整数值是可以用long类型来表示,字符串的编码设置为int。

  如果值是字符串对象,且长度大于39字节,那么编码为raw

  如果值是字符串对象,且长度小于等于39字节,那么编码为embstr

embstr的创建只需分配一次内存,而raw为两次,分别创建redisObject结构和sdshdr结构。相对地,embstr释放内存的次数也由两次变为一次。embstr的objet和sds放在一起,更好地利用缓存带来的优势。redis并未提供任何修改embstr的方式,即embstr是只读的形式。对embstr的修改实际上是先转换为raw再进行修改。

 

字符串对象是Redis五种类型的对象中唯一一种会被其他四种对象嵌套的对象。

(2)列表对象

列表对象的编码可以是ziplist或者linkedlist

当列表对象同时满足下面两个条件时,则使用ziplist:

  • 所有字符串元素的长度都小于64字节
  • 元素数量小于512

  ziplist是一种压缩列表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储。但当数据量过大时就ziplist就不是那么好用了。因为为了保证他存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。如下图所示,对象结构中ptr所指向的就是一个ziplist。整个ziplist只需要malloc一次,它们在内存中是一块连续的区域。

  linkedlist是一种双向链表。它的结构比较简单,节点中存放pre和next两个指针,还有节点相关的信息。当每增加一个node的时候,就需要重新malloc一块内存。

(3)哈希对象

哈希对象的底层实现可以是ziplist或者hashtable

当列表对象同时满足下面两个条件时,则使用ziplist:

  • 所有键值对的键和值的字符串度都小于64字节
  • 键值对数量小于512

(4)集合对象

集合对象的编码可以是intset或者hashtable

满足下面两个条件,使用intset:

  • 所以有元素都是整数值
  • 元素数量不超过512个

(5)有序集合对象

有序集合的编码可能两种,一种是ziplist,另一种是skiplist与dict的结合

为什么有序集合需要使用字典和跳表来实现?

因为字典可以实现O(1)访问,但是数据无序范围查找需要O(NlogN)时间;跳表是有序的,可以实现范围查找,但是查找键值操作时间为O(logN)。

当有序集合对象同时满足下面两个条件时,则使用ziplist:

  • 所有元素的字符串度都小于64字节
  • 元素数量小于128

二,类型检查与命令多态

Redis的命令分为两类:

(1)可以对任何类型的键执行:DEL、EXPIRE、RENAME、TYPE、OBJECT命令

(2)只能对特殊对象的键执行:

  • 类型检查的实现

在执行一个类型特定的命令之前,Redis会先检查输入键的类型是否正确,然后再决定是否执行命令。

  • 多态命令的实现

我们对一个键执行LLEN命令,服务器除了要确保执行命令的是列表键之外,还需要根据键值对象所使用的的编码来选择正确的LLEN命令实现。

实际上我们将DEL、EXPIRE、TYPE等命令也称为多态命令。这些命令和LLEN等命令的区别在于,前者是基于类型的多态———一个命令可以同时用于处理多种不同类型的键,而后者是基于编码的多态——一个命令可以同时处理多种不同的编码

三,内存回收

四,对象共享

除了用于实现引用计数内存回收机制之外,对象的引用计数属性还带有对象共享的作用。

Redis会共享值为0-9999的字符串对象,目前服务器只对包含整数值的字符串对象进行共享。

五,对象的空转时长

redisObject结构包含的最后一个属性为lru,该属性记录了对象最后一次被命令程序访问的时间。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值