Redis数据结构实现原理—对象

Redis对象介绍

  我们知道Redis的主要数据结构有简单动态字符串、双端链表、字典、压缩列表、整数集合、跳跃表等。Redis并没有直接使用这些数据结构,而是基于这些数据结构创建了一个对象系统,这些对象系统包含字符串对象、列表对象、哈希对象、集合对象、有序集合对象。对应关系如下图。
在这里插入图片描述

  此外,Redis的对象系统还实现了基于引用计数计数的内存回收机制、多个数据库之间的内存对象共享机制、记录数据库键的空转时长
  每次当我们在Redis中新增一个键值对时,Redis会创建两个对象,一个键值对的键对象,另一个是键值对的值对象。

实现

  Redis中每个对象由一个redisObject结构表示

typedef struct redisObject{
	//类型
	unsigned type;
	//编码
	unsigned encoding;
	//指向底层实现数据结构的指针
	void *ptr;
	//引用计数
	int refcount;
	//空转时长
	unsigned lru;
	//...
}
type

  redis中键值对,键肯定是一个字符串对象,值对象有以下五中,也就是我们常说的redis五种数据类型。当我们执行type命令时,redis返回的就是值对象结构中的type属性

常量对象名称
REDIS_STRING字符串对象
REDIS_LIST列表对象
REDIS_HASH哈希对象
REDIS_SET集合对象
REDIS_ZSET有序集合对象
encoding

  encoding属性记录对象所使用的编码,也就是这个对象使用的是什么数据结构作为这个对象的底层实现。可以使用OBJECT ENCODING 命令查看键值对的编码。
  通过encoding属性来设定对象所使用的编码,极大地提升了redis的灵活性和效率,因为redis可以根据不同的场景来为一个对象设置不同的编码。例如:因为压缩列表比双端链表更节约内存,并且在元素数量较少时,在内存中以连续内存块存储比分散链表形式存储读取更快而且还节省前后指针空间。
  以下为OBJECT ENCODING命令对不同编码的输出情况

底层数据结构编码常量输出
整数REDIS_ENCODING_INT“int”
embstr编码的简单动态字符串REDIS_ENCODING_EMBSTR“embstr”
简单动态字符串REDIS_ENCODING_RAW“raw”
跳跃表和字典REDIS_ENCODING_SKIPLIST“skiplist”
字典REDIS_ENCODING_HT“hashtable”
双端链表REDIS_ENCODING_INT“linkedlist”
压缩列表REDIS_ENCODING_ZIPLIST“ziplist”
整数集合REDIS_ENCODING_INTSET“intset”
refcount
  1. 对象共享,例如键A创建了一个整数值为100的字符串对象,新增键B时,直接让键B的值指针指向A的值对象,A的refcount引用计数+1。不过redis服务器初始化时会根据redis.h/REDIS_SHARED_INTEGERS创建值为0~9999的字符串对象。
  2. 内存回收,新建一个对象时,refcount值默认为1,当被一个新程序使用时,refcount加1,不在被一个程序使用时,refcount减1,当对象的refcount变为0时,对象所占用的内存会被释放。
lru

  lru属性记录了对象最后一次被命令访问的时间。OBJECT IDLETIME 命令可以输出给定键值对的空转时长,空转时长=当前时间-键lru时间。当然执行OBJECT IDLETIME时不会更新lru属性。
  lru最大用途是当服务器内存超过配置的maxmemory选项时,如果内存回收算法是volatile-lru和always-lru,空转时长较高的那部分键会被优先删除。

字符串对象

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

  • 如果字符串对象保存的是整数值并且这个值可以用long类型来表示,那么字符串对象编码为int
  • 如果字符串对象保存的是一个字符值,并且这个字符值长度大于39字节,那么字符串对象编码为raw
  • 如果字符串对象保存的是一个字符值,并且这个字符值长度小于等于39字节,那么字符串对象编码为embstr

注意:如果值为浮点数,redis会转为字符串值进行存储,使用的时候先取出字符串在进行类型转换

raw编码和embstr编码区别
  • embstr通过执行一次内存分配函数来分配一块连续的内存空间给redisObject和sdshdr两个结构,而raw通过两次来分配的
  • 释放embstr编码的字符串对象只需要调用一次内存释放函数,而raw编码的需要调用两次
  • 因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里,所以这种编码的字符串对象比raw编码的能更好地利用缓存带来的优势

列表对象

  列表对象的编码可以是ziplist或者linkedlist,同时满足以下两个条件,列表对象使用ziplist编码,否则使用linkedlist

  • 列表内元素长度都小于64字节
  • 列表对象保存的元素数量小于512个

这两个条件的配置也可以通过配置文件修改,list-max-ziplist-value和list-max-ziplist-entries选项

哈希对象

  哈希对象的编码可以是ziplist或者hashtable,同时满足以下两个条件,哈希对象使用ziplist编码,否则使用hashtable

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

这两个条件的配置也可以通过配置文件修改,hash-max-ziplist-value和hash-max-ziplist-entries选项

集合对象

  集合对象的编码可以是intset和hashtable,同时满足以下两个条件,集合对象使用intset编码,否则使用hashtable

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

这两个条件的配置也可以通过配置文件修改,set-max-ziplist-entries选项

有序集合对象

  有序集合对象的编码可以是ziplist和skiplist,同时满足以下两个条件,有序集合对象使用ziplist编码,否则使用skiplist

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

这两个条件的配置也可以通过配置文件修改,zset-max-ziplist-value和zset-max-ziplist-entries选项

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值