redis对象

Redis对象

1、对象类型与编码

​ redis使用集中基础的数据结构构建了一个对象系统,这个对象系统包含字符串对象、列表对象、哈希对象、集合对象以及有序集合对象。

​ redis中每个对象都由一个redisObject结构表示。

struct robj {
	unsigned int type;
	unsigned int encoding;
	void *ptr;
    int refcount;
    unsigned int lru;
}
  • type属性记录了对象的类型,对应下表中的常量值

    对象type属性值TYPE命令的输出
    字符串对象REDIS_STRINGstring
    列表对象REDIS_LISTlist
    哈希对象REDIS_HASHhash
    集合对象REDIS_SETset
    有序集合对象REDIS_ZSETzset
  • encoding属性设定了对象所使用的编码,对应下表中的常量值

    encoding常量值编码所对应的底层数据结构可编码的对象
    REDIS_ENCODING_INTlong类型的整数字符串对象
    REDIS_ENCODING_EMB_STRembstr编码的简单动态字符串字符串对象
    REDIS_ENCODING_RAW简单动态字符串字符串对象
    REDIS_ENCODING_HT字典哈希对象和集合对象
    REDIS_ENCODING_LINKEDLIST双向链表列表对象
    REDIS_ENCODING_ZIPLIST压缩列表哈希对象和有序列表对象
    REDIS_ENCODING_INTSET整数集合集合对象
    REDIS_ENCODING_SKIPLIST跳跃表和字典有序列表
  • ptr指针指向对象的底层实现数据结构,这些数据结构由对象的encoding属性决定

  • refcount是当前对象的引用计数,用于内存释放

  • lru记录了程序最后依次访问对象的时间,用于计算空转时长

2、字符串对象

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

​ 如果一个字符串对象保存的是整数值且这个整数可以用long表示,则字符串对象会将整数值保存在字符串对象结构的ptr属性里边(将void *换为long),并将字符串对象的编码设置为int;若一个字符串对象保存的是一个字符串值且长度小于等于32字节,则将使用embstr编码的方式保存字符串;若一个字符串对象保存的是一个字符串且长度大于32字节,则将使用raw格式的sds保存字符串。

  • embstr编码

​ embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw一样,都使用redisObject和sdshdr结构表示字符串对象,但raw编码会调用两次内存分配函数来分别创建redisObject和sdshdr结构,而embstr只会调用一次内存分配函数来分配一块连续的内存空间,空间中依次包括redisObject和sdshdr结构。embstr编码的好处如下:

  • embstr将raw编码的两次内存分配、两次内存释放降低为一次

  • embstr编码的字符串对象的所有数据保存在一块连续的内存空间里,能够更好地利用缓存的优势

  • 编码之间的互相转换

    int和embstr编码的字符串在满足特定条件的情况下会被转换为raw编码的字符串对象。当对int编码类型的字符串执行了一定的操作使得字符串对象不再是整数时,编码将会从int转换为raw;当对embstr编码类型的字符串执行了任何修改操作后,编码将会从embstr转换为raw。

3、列表对象

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

​ ziplist编码可以作为列表对象的底层数据结构,每个压缩列表的entry节点保存一个列表元素。

  • 编码的转化

    当列表对象保存的所有字符串元素长度都小于64个字节元素个数小于512个时,列表使用ziplist编码;否则,使用linkedlist编码。两种编码方式会按照上述条件自动转换。

    注意:以上两个条件中的上限值是可以修改的,分别受redis配置文件中list-max-ziplist-valuelist-max-ziplist-entries选项的控制。

4、哈希对象

​ 哈希对象的编码可以是ziplisthashtable

​ ziplist编码可以作为哈希对象的底层数据结构,每当有新的键值对要添加到哈希对象中时,程序会先将保存了键的压缩列表节点push到压缩列表表尾,然后再将保存了值的压缩列表节点push到压缩列表表尾。这样操作之后,保存了同一键值对的两个节点总是紧挨在一起,前一个节点代表键,后一个节点代表值;先添加到哈希对象中的键值对保存在压缩列表表头方向,后添加的键值对保存在压缩列表表尾方向。

  • 编码的转化

    当哈希对象中保存的所有键值对的键和值的字符串长度都小于64个字节键值对数量小于512个时,使用ziplist编码;否则,使用hashtable编码。两种编码方式会按照上述条件自动转换。

    注意:以上两个条件中的上限值是可以修改的,分别受redis配置文件中hash-max-ziplist-valuehash-max-ziplist-entries选项的控制。

5、集合对象

​ 集合对象的编码方式可以是intsethashtable

​ intset可以作为集合对象的底层数据结构,集合对象中的所有元素都被保存在整数集合中;hashtable是集合对象使用的另一种数据结构,字典的键是集合对象中的元素,字典的值都为null。

  • 编码的转换

    当集合对象中保存的所有元素都是整数值元素个数不超过512个时,使用intset编码方式;否则,使用hashtable编码。两种编码方式会按照上述条件自动转换。

    注意:以个条件中的上限值是可以修改的,受redis配置文件中set-max-intset-entries选项的控制。

6、有序集合对象

​ 有序集合对象的编码方式可以是ziplistskiplist

​ ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点保存,第一个节点保存元素的成员,第二个节点保存元素的分值。压缩列表内的集合元素按照分值从小到大进行排序,分值较小的元素放在靠近表头的位置,分值较大的放在靠近表尾的位置。

​ skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个跳跃表和一个字典。

struct zset {
	zskiplist *zsl;
	dict *dict;
}

​ zset中的跳跃表zsl按照分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合对象,跳跃表节点的object属性保存了元素的成员,score属性保存了分值;通过跳跃表可以对分值进行范围操作。zet中的字典dict保存了有序集合中所有元素从成员到分值的映射关系,通过字典可以使用O(1)的复杂度取出某个成员的分值。

  • 编码的转换

    当有序集合中所有成员的长度都小于64字节保存的元素数量小于128个时,使用ziplist编码;否则,使用skiplist编码。两种编码方式会按照上述条件自动转换。

    注意:以上两个条件中的上限值是可以修改的,分别受redis配置文件中zset-max-ziplist-valuezset-max-ziplist-entries选项的控制。

7、内存回收与对象共享

内存回收

​ redis在自己的对象系统中构建了一个引用计数技术实现内存挥手机制,通过这一机制,程序可以跟踪对象的引用计数信息,在适当的时候释放对象所占用的内存空间。

​ 对象的整个生命周期可以划分为三个阶段:创建对象、操作对象、释放对象。当创建一个对象时,引用计数初始化为1;当对象被一个新程序使用时,引用计数加一;当对象不再被一个程序使用时,引用计数减一;当对象的引用计数变为0时,对象所占用的内存会被释放。

对象共享

​ 对象的引用计数还有对象共享的作用。多个键所对应的值相同时,可以共享同一个字符串对象,将引用计数加上相应的增量即可。共享机制对于节约内存非常有用,数据库中保存的相同值对象越多,就能节约越多的内存。

​ 对于redis2.8来说,redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当要用到这些字符串对象时,服务器会使用这些共享对象,而不是在创建新对象。

​ redis不共享包含字符串的对象,因为在共享对象前程序需要检查共享对象和目标对象是否完全相同,如果共享对象是整数值的字符串对象,则复杂度为O(1);如果共享对象是字符串值的字符串对象,则复杂度为O(N);如果共享对象是包含了多个值对象的对象(如列表或hash等),则复杂度是O(N*N)。尽管共享更复杂的对象可以节约更多的内存,但收到CPU时间的限制,redis只会对包含整数值的字符串对象进行共享。

对象的空转

​ redisObject中的lru属性记录了程序最后依次访问对象的时间,通过将当前时间减去键的值对象的lru时间可以计算出值对象的空转时间。使用OBJECT IDLETIME key可以打印键key对应的值对象的空转时长,OBJECT IDLETIME命令在访问键的值对象时,不会改变lru。

​ 如果服务器打开了maxmemory选项且服务器用于回收内存的算法为 volatile-lruallkeys-lru,那么当服务器 使用的内存超过了maxmemory时,服务器会优先释放空转时长比较高的那部分键值对,以回收内存。

参考文献

命令在访问键的值对象时,不会改变lru。

​ 如果服务器打开了maxmemory选项且服务器用于回收内存的算法为 volatile-lruallkeys-lru,那么当服务器 使用的内存超过了maxmemory时,服务器会优先释放空转时长比较高的那部分键值对,以回收内存。

参考文献

redis设计与实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值