Redis的对象

Redis基于所用到的主要数据结构创建了一个对象系统,包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象。根据对象的类型可以判断一个对象是否可以执行给定的命令,也可针对不同的使用场景,对象设置有多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。

对象的类型与编码

Redis中的每个对象都是由如下结构表示(列出了与保存数据有关的三个属性)

typedef struct redisObject {
	unsigned type:4;//类型 五种对象类型
	unsigned encoding:4;//编码
	void *ptr;//指向底层实现数据结构的指针
	//...
	int refcount;//引用计数
	//...
	unsigned lru:22;//记录最后一次被命令程序访问的时间
	//...
}robj;

1、类型

  • Redis的键值对中,键总是字符串对象,值可以是五种对象类型之一
  • TYPE命令:

set msg “hello”
OK
type msg
string

不同类型值对象的TYPE命令输出

对象type属性的值TYPE命令输出
字符串对象REDIS_STRING“string”
列表对象REDIS_LISTlist
哈希对象REDIS_HASHhash
集合对象REDIS_SETset
有序集合对象REDIS_ZSETzset

2、编码和底层实现

  • encoding属性记录了对象所使用的编码
  • 每种类型的对象都至少使用了两种不同的编码
  • OBJECT ENCODING命令:

SET msg “hello”
OK
OBJECT ENCODING msg
“embstr”

不同类型和编码的对象

类型编码OBJECT ENCODING命令输出对象
REDIS_STRINGREDIS_ENCODING_INT“int”使用整数值实现的字符串对象
REDIS_STRINGREDIS_ENCODING_EMBSTR“embstr”使用embstr编码的简单动态字符串实现的字符串对象
REDIS_STRINGREDIS_ENCODING_RAW“raw”使用简单动态字符串实现的字符串对象
REDIS_LISTREDIS_ENCODING_ZIPLIST“ziplist”使用压缩列表实现的列表对象
REDIS_LISTREDIS_ENCODING_LINKEDLIST“linkedlist”使用双端链表实现的列表对象
REDIS_HASHREDIS_ENCODING_ZIPLIST“ziplist”使用压缩列表实现的哈希对象
REDIS_HASHREDIS_ENCODING_HT“hashtable”使用字典实现的哈希对象
REDIS_SETREDIS_ENCODING_INTSET“intset”使用整数集合实现的集合对象
REDIS_SETREDIS_ENCODING_HT“hashtable”使用字典实现的集合对象
REDIS_ZSETREDIS_ENCODING_ZIPLIST“ziplist”使用压缩列表实现的有序集合对象
REDIS_ZSETREDIS_ENCODING_SKIPLIST“skiplist”使用跳跃表和字典实现的有序集合对象

encoding属性来设定对象所使用的编码,没有关联一种固定的编码,极大提升Redis的灵活性和效率。

字符串对象

1、字符串对象编码选择

  • 如果保存的是整数值,字符串对象的编码设置为int
  • 如果保存的是一个字符串值,并且这个字符串值的长度大于32字节,对象的编码设置为raw,使用SDS保存
  • 如果保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,对象的编码设置为embstr(专门用于保存短字符串的一种优化编码方式)

2、embstr比raw方式的优势

  • embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次
  • 释放embstr编码的字符串对象只需要调用一次内存释放函数
  • embstr编码的字符串对象所有数据都保存在一块连续的内存里面,能够更好地利用缓存带来的优势

3、long double 类型浮点数保存(embstr或raw)

  • long double 类型浮点数是作为字符串值保存的,先将浮点数转为字符串值,然后再保存,需要用到的时候再将对象里面的字符串值转为浮点数值,然后再操作,操作完再转换回来保存

4、编码转换

  • int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象
  • 如果对int编码的字符串对象进行了一些操作使得对象保存的不再是整数值,则会转换
  • embstr编码的字符串对象在执行修改命令之后,总会变成一个raw编码的字符串对象

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

列表对象

1、列表对象编码可以是ziplist或linkedlist

2、编码转换(限制条件的上限值可以修改)

  • 使用ziplist编码同时满足两个条件:列表对象保存的所有元素长度都小于64字节;列表对象保存的元素数量小于512个
  • 不能满足上述条件的列表对象使用linkedlist编码,当不满足时编码转换操作会被执行,压缩列表里的所有列表元素转移并保存到双端链表里面

哈希对象

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

2、ziplist编码的哈希对象使用压缩列表作为底层实现:

  • 保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值得节点在后
  • 先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向

3、hashtable编码的哈希对象使用字典作为底层实现:

  • 字典的每个键都是一个字符串对象,对象中保存了键值对的键
  • 字典的每个值都是一个字符串对象,对象中保存了键值对的值

4、编码转换(限制条件的上限值可以修改)

  • 哈希对象使用ziplist编码同时满足条件:哈希对象保存的所有键值对的键和值得字符串长度都小于64字节;哈希对象保存的键值对数量小于512个
  • 未能满足上述条件的使用hashtable编码,当不满足时编码转换操作会被执行,压缩列表里的所有列表元素转移并保存到字典里面

集合对象

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

2、intset编码的集合对象使用整数集合作为底层实现

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

4、编码转换(限制条件的上限值可以修改)

  • 使用intset编码同时满足条件:集合对象保存的所有元素都是整数值;集合对象保存的元素数量不超过512个
  • 未能满足上述条件的使用hashtable编码,当不满足时编码转换操作会被执行,整数集合里的所有列表元素转移并保存到字典里面

有序集合对象

1、有序集合的编码可以是ziplist或skiplist

2、ziplist编码的压缩列表对象使用压缩列表作为底层实现

  • 每个集合元素使用两个紧挨在一起的压缩泪飚节点来保存,第一个节点保存元素的成员,第二个元素则保存元素的分值
  • 压缩列表内的集合元素按分值从小到大进行排序

3、skiplist编码的有序集合对象使用zset结构作为底层实现

  • 一个zset结构同时包含一个字典和一个跳跃表
typedef struct zset {
	zskiplist *zsl;//按分值从小到大保存所有集合元素,对有序集合进行范围型操作
	dict *dict;//为有序集合创建了一个从成员到分值的映射,可以用O(1)复杂度查找给定成员的分值
}zset;
  • 有序集合每个元素的成员都是一个字符串对象,而每个元素的分值都是一个double类型的浮点数
  • 虽然zset结构同时使用跳跃表和字典来保存有序集合元素,但这两种数据结构都是通过指针来共享相同元素的成员和分值,不会浪费额外的内存

4、编码转换(限制条件的上限值可以修改)

  • 使用ziplist编码同时满足条件:有序集合保存的元素数量小于128个;有序集合保存的所有元素成员长度都小于64字节
  • 未能满足上述条件的使用skiplist编码,当不满足时编码转换操作会被执行,压缩列表里的所有列表元素转移并保存到zset结构里面

类型检查与命令多态

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

2、只能对特定类型的键执行的命令:

  • 字符串键:SET GET APPEND STRLEN等
  • 哈希键:HDEL HSET HGET HLEN等
  • 列表键:RPUSH LPOP LINSERT LLEN等
  • 集合键:SADD SPOP SINTER SCARD等
  • 有序集合键:ZADD ZCARD ZRANK ZSCORE等

3、类型特定命令在执行之前会先检查输入键的类型是否正确,是通过redisObject结构的type属性来实现的

4、多态命令实现:根据值对象的类型来判断键是否能够执行指定命令,根据值对象的编码方式,选择正确的命令实现代码来执行命令

内存回收

Redis在自己的对象系统中构建了一个引用计数计数实现内存回收机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收

对象共享

1、对象的引用计数属性还带有对象共享的作用

2、让多个键共享同一个值对象需要执行:将数据库键的值指针指向一个现有的值对象;将被共享的值对象的引用计数增一

3、尽管共享更复杂的对象可以节约更多的内存,但受到CPU时间的限制,Redis只对包含整数值得字符串对象进行共享(完全相同的才能共享,验证相同时的复杂度决定的)

4、Redis会共享值为0~9999的字符串对象

对象的空转时长

1、对象会记录自己的最后一次被访问的时间,可以用于计算对象的空转时间

2、OBJECT IDLETIME命令可以打印给定键的空转时长=当前时间-lru(该命令本身不会更新lru值)

3、超过maxmemory选项上限,在有些回收内存算法下,空转时长较高的那部分键会优先被服务器释放

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值