Redis设计与实现笔记--对象

对象

Redis没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象五种类型的对象。

特性

  • 根据对象类型可以判断一个对象是否可以执行给定的命令
  • 针对不同使用场景,为对象设置多种不同的数据结构实现,优化对象在不同场景下的使用效率
  • 基于引用计数的内存回收机制,当程序不再使用某个对象的时候,所占用的内存就会自动释放
  • 通过引用计数技术实现了对象共享机制,在适当条件下,让多个数据库键共享同一个对象来节约内存
  • 对象带有访问时间记录信息,可以用于计算数据库键的空转时长,在服务器启用了maxmemory功能下,空转较大的键会优先被服务器删除
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向低层实现数据结构的指针
void *ptr;
// 引用计数
int refcounf;
// 最后一次被命令访问时间
unsigned lru:22;
...
} robj; 

对象共享

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

目前来说(本书基于redis2.9),Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,就会使用这些共享对象,而不是新创建对象。

为什么不共享包含字符串的对象?共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需的复杂度就会越高,消耗cpu时间也会越多。保存整数值的共享对象,验证操作复杂度为O(1)。字符串值为O(N)。包含多个值,如列表或哈希为O(N^{2})。受到CPU时间限制,Redis只对整数值字符串对象进行共享

五大对象

Object encoding命令可以查看一个数据库键的值对象的编码:

通过encoding属性设定对象所使用的编码,而不是特定类型的对象关联一种固定的编码,极大地提升了Redis的灵活性和效率。如: 在列表对象包含的元素比较少时,Redis使用压缩列表作为列表对象的底层实现。

  • 因为压缩列表笔双端链表更节约内存,并且在元素数量较少时,在内存中以连续块方式保存的压缩列表比起双端链表可以更快被载入到缓存中。
  • 随着列表对象包含元素越来越多,对象就会从压缩列表转向功能更强,也更适合保存大量元素的双端链表上面。

字符串对象

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

编码转换

  • 字符串值长度大于32字节,将使用一个SDS保存字符串值,并将对象编码设置为raw。
  • 小于等于32字节,将使用embstr编码的方式来保存这个字符串值。

embstr和raw编码一样,都使用redisObject和sdshdr结构来表示字符串对象,但raw会调用两次内存分配函数来分别创建redisObject和sdshdr。而embstr编码则通过一次内存分配来分配一块连续的空间。空间中依次包含这两个结构。

好处:

  • 内存分配次数从raw编码的两次降低为一次
  • 释放embstr只需要一次内存释放函数,raw需要两次
  • 因为embstr在一块连续的内存里面,所以比raw编码对象能够更好的利用缓存带来的优势

 

列表对象

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

编码转换

当列表对象可以同时满足一下两个条件时,列表对象使用ziplist编码:

  • 列表对象保存的所有字符串长度都小于64字节
  • 列表对象保存的元素数量小于512

不能满足这两个条件的列表对象需要使用linkedlist编码。

以上两个上限值可通过list-max-ziplist-value和list-max-ziplist-entries配置

哈希对象

哈希对象的编码可以是ziplist或者hashtable

哈希对象的压缩列表底层实现:

编码转换

当哈希对象可以同时满足一下两个条件时,哈希对象使用ziplist编码:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
  • 哈希对象保存的键值对数量小于512

不能满足这两个条件的列表对象需要使用hashtable编码。

以上两个上限值可通过hash-max-ziplist-value和hash-max-ziplist-entries配置

集合对象

集合对象的编码可以是intset或者hashtable。intset编码的集合对象使用整数集合作为底层实现。

hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL

编码的转换

当集合对象可以同时满足以下两个条件时,对象使用intset编码:

  • 集合对象保存的所有元素都是整数值
  • 集合对象保存的元素数量不超过512个

不能满足这两个条件的集合对象需要使用hashtable编码

第二个条件可通过set-max-intset-entries配置

有序对象集合

有序结合的编码可以是ziplist或者skiplist

ziplist实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存

skiplist实现,一个zset结构同时包含一个字典和一个跳跃表

注意:图中为了展示方便,重复展示了各个元素的成员和分值。实际中,字典和跳跃表会共享元素。

为什么同时使用跳跃表和字典来实现

无论单独使用字典还是跳跃表实现,性能上都会比同时使用字典和跳跃表都会有所降低。

字典以O(1)复杂度查找成员,但以无序的方式来保存结合元素。所以每次范围操作,完成排序至少需要O(NlogN)时间复杂度,以及额外的O(N)内存空间。

跳跃表执行范围型操作的所有优点都会被保留,但因为没有了字典,所以根据成员查找分值复杂度从O(1)上升为O(logN)。为了让有序集合的查找和范围型操作都尽可能快的执行,Redis选择了同时使用字典和跳跃表两种数据结构来实现有序集合。

编码的转换

当有序集合对象可以同时满足以下两个条件时,对象使用ziplist编码:

  • 有序集合保存的元素数量小于128个
  • 有序集合保存的所有元素成员的长度都小于64字节

不能满足以上两个条件的有序集合对象将使用skiplist编码

以上两个上限值可通过zset-max-ziplist-value和zset-max-ziplist-entries配置

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值