文章目录
在redis中,对象就是封装了数据结构的实现.
对象的数据结构
typedef struct redisObject {
//类型
unsigned type:4;
//编码
unsigned encoding:4;
//记录对象最后一次访问时间,用于垃圾回收
//可通过OBJECT IDLETIME 查看给定键的空转时长(这个命令不会修改LRU时间),也就是多久没访问.
//根据lru属性,若服务器回收内存算法为volatile-lru或者all-lru,且服务器内存占用超过maxmemory,则lru时间长的就会被回收.
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
//引用计数 * and most significant 16 bits access time). */
int refcount;
//指向底层数据结构的指针
void *ptr;
} robj;
类型
当存一对key和value时,分别创建2个对象,键对象始终为字符串类型,而value可以是如下类型之一.
可使用Type命令查看类型
编码
ptr具体指向那个数据结构,取决于encoding
每种数据结构对应编码如下图所示:
每种类型的对象可以使用的编码,如下图所示:
可使用OBJECT ENCODING查看编码类型
空转时长
可通过OBJECT IDLETIME 查看给定键的空转时长(这个命令不会修改LRU时间),也就是多久没访问.
根据lru属性,若服务器回收内存算法为volatile-lru或者all-lru,且服务器内存占用超过maxmemory,则lru时间长的就会被回收.
内存回收与内存共享
生命周期如下:
- 创建时,引用计数初始化1
- 被新程序引用,则引用计数+1
- 不在引用,则-1
- 为0,则释放内存
当对象共享时,引用计数也会+1。目前redis只共享包含整数值的字符串对象.因为共享数字,只需要拿出value对比就行了,复杂度为O(1)。而字符串的话需要遍历每个字符串,复杂度为O(N).如果包含多个值,那么就需要嵌套循环遍历.复杂度为O(N^2).
注意:redis会共享0~9999的字符串对象
当创建一个对象,会有2个引用.
可通过OBJECT REFCOUNT查看引用次数.
字符串对象
字符串对象的编码可以是int,embstr,raw.
注意:long double浮点数也是字符串存储的.
int
使用该编码的前提条件:
- 保存的是整数值
- 可被long类型表示.
保存步骤:将整数保存ptr(将void*变成long),并将属性编码设置int
raw
前提条件:
- 保存的是字符串
- 长度>32字节
使用SDS保存
embstr
前提条件:
- 保存的是字符串
- 长度<=32字节
步骤:和raw一样,不过区别在于raw需要分别为redisObject和sds结构分配内存,而embstr则只需要分配一次就行.分配的空间redisObject和sds相邻.
优点:
- 与raw相比较,分配资源少了一次
- 释放内存也少了一次
- 因为2对象相邻,读起来不需要寻址咯.
编码何时转换?
存的是int类型,当执行appen类似命令时,会先转换成字符串,然后执行append操作.
embstr执行append类似也同上.所以embstr只支持只读.
命令以及实现
列表对象
ziplist
前提条件:
- 列表对象保存的所有字符串元素的长度<64字节.可通过list-max-zpilist-value配置
- 元素数量<512.可通过list-max-zpilist-entries配置
当插入三个数据
结构如下:
linkedList
命令以及实现
hash对象
ziplist
前提条件:
- 保存的所有键和值的字符串长度<64.可通过hash-max-zpilist-value配置
- 键值对数量<512,可通过hash-max-zpilist-entries配置
步骤:键存压缩列表尾部,紧接着值存队列尾部
存储了三个键值对的结构样子:
hashtable
命令以及实现
集合对象
intset
前提条件:
- 保存的值为整数
- 值数量<512,可通过set-max-intset-entries配置
hashtable
键为存的集合值,值为null.
和hashSet一样,键为集合值,value为Object而已.
命令以及实现
有序集合
ziplist
前提条件:
- 保存的每个成员长度 < 64byte,可通过zset-max-zpilist-value配置
- 数量<128,可通过zset-max-zpilist-entries 配置
skiplist
分别采用跳跃表+字典方式保存.
//实现有序集合
//在这里,字典表和跳跃表每个成员和score,都共享.所以不会浪费内存
typedef struct zset {
//存储key-score,这样根据key查找,复杂度为O(1)
dict *dict;
//根据score,有序存储.执行范围查找时,就无须排序.
zskiplist *zsl;
} zset;
命令以及实现
多台命令实现
redis有2种命令,一种是五种数据类型都可使用的命令,如DEL,另外一种是五种数据类型特定的命令.
在执行命令时,redis会看看是否与类型匹配,不匹配直接异常,
如果匹配则,再次查看数据结构实现,根据不同的数据结构实现选择不同的实现方法.