Redis数据结构之Redis对象
Redis基本数据类型
Redis提供了5个基本数据类型 : 字符串对象 (string)、列表对象 (list)、哈希对象 (hash)、集合对象 (set)
、有序集合对象 (zset)。这五种基本数据对象都至少使用了之前介绍的一种Redis编码方式。
Redis对象
Redis中每个对象都由一个 redisObject 结构表示:
typedef struct redisObject {
//数据类型
unsigned type:4;
//数据编码
unsigned encoding:4;
//指向底层数组数据结构的指针
void *ptr;
//对象引用计数
unsigned refcount:4;
//对象空转时长
unsigned lru:22;
} robj;
举例 :
当我们在Redis中执行一个 Set msg “hello world” 时会创建一个键对,Redis底层会创建两个redisObject对象。
其中键值对的键是一个包含了 “msg” 的对象,键值对的值则是一个包含了字符串值 “hello world”的对象。下图是键msg的底层对象描述
typedef struct redisObject {
//数据类型
unsigned type = string;
//数据编码
unsigned encoding = sds;
//指向底层数组数据结构的指针
void *ptr -> hello world;
//对象引用计数
unsigned refcount = 1;
//对象空转时长
unsigned lru = 20;
} robj;
Redis对象之type属性
redis对象的type属性记录了对象的类型,对于Redis数据库保存的键值对来说,键总是一个字符串对象,而值可以是
Redis提供的五种基本数据类型中一种。因此当我们称Redis中某个key为list对象时,其实指的是这个Key中的所对应的值
为list对象。与type属性相对应的是Redis提供的 type 命令。 type命令返回的结果为数据键对应的值对象的类型而不是键
对象的类型。 Redis命令为 : type [key]
Redis对象之encoding 、*ptr 属性
Redis对象的ptr指针指向对象的底层实现数据结构, 而这些数据结构由对象的encoding属性决定. encoding属性记录了对象所使用的编码,也即这个对象使用了什么数据结构作为对象的底层实现
encoding属性可选编码值 :
编码常量 编码所对应的底层数据结构
REDIS_ENCODING_INT long类型的整数
REDIS_ENCODING_EMBSTR embstr编码的简单动态字符串
REDIS_ENCODING_RAW 简单动态字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 双端链表
REDIS_ENCODING_ZIPLIST 压缩列表
REDIS_ENCODING_INTSET 整数集合
REDIS_ENCODING_SKIPLIST 跳跃表和字典
与encoding属性相对应的是Redis提供的 encoding 命令。encoding命令返回的结果是值对象所使用编码
Redis 命令 : object encoding [key]
每种类型的对象都至少使用了两种不同的编码
字符串对象编码:
编码 对象
REDIS_ENCODING_INT (int) 使用整数值实现的字符串对象
REDIS_ENCODING_EMBSTR (emstr) 使用embstr编码的简单动态字符串实现的字符串对象
REDIS_ENCODING_RAW (raw) 使用简单动态字符串实现的字符串对象
1. 如果字符串对象保存的是整数值,并且这个整数可以用long类型表示。那么字符串会使用int编码类型(将*ptr转为long)
2. 如果字符串对象保存的是字符串,并且字符串值长度小于等于32字节。那么字符串会使用embstr编码保存字符串。
3. 如果字符串对象保存的是字符串,并且字符串值长度大宇32字节。那么字符串会使用raw编码保存字符串。
embstr编码是专门用来保存短字符串的一种优化编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构来
表示字符串对象。但是raw编码会调用两次内存分配函数分别创建 redisObject结构和sdshdr结构,而embstr编码则通过调用一
次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构。在释放内存时embstr编码只需要调用
一次内存释放函数而raw需要调用两次内存释放。embstr与raw相比能够更好的利用缓存带来优势。需要注意的是Redis没有为
embstr编码提供相应的修改操作,也就是说embstr是只读的。当对embstr编码字符串修改时会先将对象的编码从embstr转为
raw,然后再执行次修改命令。long、double类型表示的浮点数在Redis中也是作为字符串来表示的,如果要保存浮点数到字符
串对象里,redis会将浮点数转为字符串值保存起来。在有需要的时候Redis又会将字符串转为浮点数值执行某些操作,再将操
作所得的浮点数值转回字符串值。
列表对象编码 :
列表对象编码 : ziplist、linkedlist
当列表对象保存的字符串满足下面两个条件时底层使用ziplist编码不满足这两个条件中任意一个时则会使用linkedlist编码
1.元素的长度都小于64字节
2.元素数量小于512个
以上两个条件的上限值是可以修改的,可以通过修改redis配置文件中的
list-max-ziplist-value、list-max-ziplist-entries选项进行修改
哈希对象编码 :
哈希对象编码 : ziplist、hashtable
当哈希对象保存的字符串满足下面两个条件时底层使用ziplist编码不满足这两个条件中任意一个时则会使用hashtable编码
1.键值对的键和值的长度都小于64字节
2.键值对数量小于512个
以上两个条件的上限值是可以修改的,可以通过修改redis配置文件中的
hash-max-ziplist-value、hash-max-ziplist-entries选项进行修改
集合对象编码 :
集合对象编码 : intset、hashtable
当集合对象保存的字符串满足下面两个条件时底层使用intset编码不满足这两个条件中任意一个时则会使用hashtable编码。当使用hashtable编码时,set集合中的值保存在字典的键上,而字典的值为null。
1.集合对象保存的所有元素都是整数值
2.集合对象保存的元素数量小于512个
以上第二个条件的上限值是可以修改的,可以通过修改redis配置文件中的
set-max-ziplist-entries选项进行修改
有序集合对象编码 :
有序集合对象编码 : ziplist、skiplist
当有序集合对象保存的字符串满足下面两个条件时底层使用ziplist编码不满足这两个条件中任意一个时则会使用skiplist编码。
1.有序集合对象保存的所有元素长度都小于 64字节
2.集合对象保存的元素数量小于128个
以上第二个条件的上限值是可以修改的,可以通过修改redis配置文件中的
zset-max-ziplist-value、zset-max-ziplist-entries选项进行修改
当有序集合使用ziplist编码时,压缩列表内的集合元素按分值从小到大进行排序,分值较小的被放在靠近表头的方向,而分值较大的元素则被放在靠近表尾的方向。
当有序集合使用skiplist编码时,一个zset结构同时包含一个字典和一个skiplist
typedef struct zset {
zskiplist * zsl;
dict *dict;
}zset;
zset结构中的zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素;跳跃表节点的object属性
保存了元素成员,而跳跃表节点的score属性则保存了元素的分值。通过这个跳跃表可以对有序集合进行范围行操作,比如zrank、
zrange. zset结构中的dict字典为有序集合创建了一个成员到分值的映射,字典的键保存了元素的成员,字典的值保存了元素的
分值。通过这个字典,程序可以用O(1)复杂度查找给定成员的分值,ZSCORE命令就是根据这一特性实现的。虽然zset结构同
时使用跳跃表和字典来保存有序集合元素,但这两中数据结构都会通过指针来共享相同元素的成员和分值,所以同时使用跳跃
表和字典保存集合元素不会产生任何重复成员或分值,也不会因此浪费内存。
Redis对象之refcount属性
因为C语言并不具备自动内存回收功能,所以Redis在自己的对象系统中构建了一个引用计数计数实现的内存回收机制,
通过这一机制程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对方并进行内存回收。除了内存回收外,对
象的refcount属性还带有对象共享的作用。假设有1万个key的值都是整数100,那么Redis只需要用一个字符串对象的内存
就可以保存一万个字符串对象的内存才能保存的数据。Redis会在初始化服务器时创建1万个字符串对象 (0~9999的所有整
数值),当服务器需要用到0到9999的字符串对象时Redis会直接共享这些对象而不是创建对象。另外,这些共享对象不单单
只有字符串可以使用,那些在数据结构中嵌套了字符串对象linkedlist/hashtable/zset都可以使用这些对象
与refcount属性对应的Redis命令是 object refcount [key]
Redis对象之lru属性
lru属性记录了对象最后一次被命令程序访问的时间。object idletime 命令可以打印出给定键的空转时
长,这已空转时长就是通过当前时间减去对象的lru时间计算得出的。object idletime命令不会修改对象的lru
属性。如果服务器打开了maxmemory选项,并且使用XXX-lru算法,那么当服务器占用的内存超过最大内存时,空转时长
较高的那部分键会优先被服务器释放掉,从而回收内存。