redis对象
概述
redis基于简单动态字符串、双端链表等基本数据结构,创建了一个对象系统。
包含字符串对象,列表对象,哈希对象,集合对象,有序集合对象。
Redis的对象系统还实现了基于引用计数技术的内存回收机制
对象的类型和编码
redis对象包含如下属性
1 | typedef struct redisObj{ |
类型
type命令可以查看对象的类型,对应关系如下:
编码和底层实现
一个对象的底层实现可能有多种方式,比如列表对象底层可以是压缩链表或者双端链表实现,对应关系如下。
object encoding命令可以查看一个数据库键的值对象编码.
OBJECT ENCODING对不同编码的输出

字符串对象
字符串对象的编码可以是:
- int:字符串对象保存的是整数值,并且这个整数值可以用long类型表示。
- raw:字符串对象保存的是一个长度大于39字节
- embstr:字符串对象保存的是一个长度小于等于39字节,此编码是专门用于保存字段字符串的一种优化编码方式。
raw编码会调用两次内存分配函数分别创建redisObj结构和sdshdr结构,而embstr只调用一次内存分配函数来分配一块连续的空间,空间中一次包含redisObj和sdshdr。
使用embstr保存短字符串好处如下:
减少一次内存分配函数的调用
减少一次内存释放函数的调用
所有数据在一块连续的内存里面,能够更好地利用缓存带来的优势
int编码的字符串对象

raw编码的字符串对象

embstr编码的字符串对象

ps:可以用long double类型表示的浮点数在redis中使用字符串值来保存的。(c中的long double类型为128位,范围比double大)
字符串对象保存各类型的编码方式
编码的转换
int编码的字符串对象,当这个对象保存的不在是整数值,而是字符串,编码会变为raw。
embstr编码的字符串对象是只读的,当对它进行任何修改命令的时候,程序会把编码编程raw,再执行修改命令。
字符串对象是唯一一个会被其他对象嵌套的对象。
列表对象
列表对象的编码可以是ziplist或者linkedlist。
当列表对象同时满足一下两个条件时,列表对象用ziplist编码
- 列表对象保存的所有字符串元素的长度都小于等于64字节(阈值可修改)
- 列表对象保存的元素小于等于512个(阈值可修改)
列表对象3.2版本之后
Redis中的列表对象在版本3.2之前,列表底层的编码是ziplist和linkedlist实现的,但是在版本3.2之后,重新引入了一个 quicklist 的数据结构,列表的底层都由quicklist实现。
哈希对象
哈希对象的编码可以是ziplist或者hashtable。
压缩列表作为哈希对象的底层实现,每当有新的键值对要加入到哈希对象时,程序会将键压入压缩列表表尾,再将值压入压缩列表的表尾。

hashtable编码底层用字典实现。
编码转换
当哈希对象同时满足以下两个条件是,哈希对象使用ziplist编码
- 所保存的所有键值对的键和值的字符串长度都小于等于64字节
- 所保存的键值对数量小于等于512个(非键和值的总和小于512)
集合对象
集合对象的编码可以是intset或者hashtable。
编码转换
当集合对象同时满足以下两个条件时,对象使用intset编码(整数集合),否则用hashtable编码。
- 保存的元组都是真数值
- 保存元素的数量不超过512个
有序集合对象
有序集合的编码可以是ziplist或者skiplist。
ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个保存元素成员,第二个保存元素的分值,压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的位置。

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

程序可以对集合进行范围型操作,ZRANK、ZRANGE等命令是基于跳跃表的api实现的。
字典的键保存了元素成员,值保存了元素的分值,通过这个字典,可以用O(1)的时间复杂度查找给定成员的分支。
为什么有序集合需要同时使用跳跃表和字典实现?
- 单独使用跳跃表,当需要根据成员查找分值的时候,需要遍历整个跳跃表,时间复杂度从O(1)上升到O(logN)。
- 单独使用字典,则对元素进行排序等操作,至少需要O(NlogN)时间复杂度和额外的O(N)内存空间。
同时使用跳表和字典,则兼容了两种数据结构的优点,并且两种数据结构通过指针共享相同元素的成员和分值,不会因此浪费额外的内存。
编码转换
当有序集合对象同时满足以下两个条件时,对象使用ziplist编码。
- 保存的元素数量小于128个
- 保存元素的成员的长度小于64字节
内存回收
通过引用计数法,适当的时候自动释放对象并进行内存回收。
对象共享
redis4.0 为了支持惰性删除,redis彻底废弃了对象共享机制
Redis在初始化服务器的时候,创建一万个字符串对象,这些字符串对象包含了0-9999的所有整数值,这些对象是会被共享的,不会重复创建。
Redis不共享其他字符串的对象?
一个共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需要的复杂度就越高,消耗的CPU时间越多。
- 如果共享对象是保存整数值的字符串对象,验证操作的时间复杂度为O(1)
- 如果共享对象保存的是字符串值的字符串对象,验证操作的时间复杂度为O(n)
- 如果是共享对象是复杂对象,例如列表对象,那么验证操作的时间复杂度为O($n^2$)
对象空转时长
lru属性记录了对象最后一次被访问的时间。
OBJECT IDLETIME命令可以打印给定键的空转时长,且不会更新它的lru时间。