redis的对象
一、对象
在redis里并没有直接使用数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统
这个系统包含字符串对象,列表对象、哈希对象、集合对象和有序集合对象这五种类型
组成:
- type:类型,占用四个字节
- encoding:编码,占用四个字节
- void *ptr:指向底层实现数据结构的指针
二、字符串对象
字符串对象的编码可以是int、raw和embstr
- 如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串会将整数值保存在字符串对象结构的ptr属性里面(将void *改成 long),并将字符串的编码改为int。
- 如果字符串对象保存的是一个字符串值,并且这个字符串值长度大于32字节,那么字符串对象将使用一个简单的动态字符串(SDS)来保存这个值,设置对象编码为raw
- 如果字符串对象保存的是一个字符串值,并且这个字符串值长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串
embstr和int在条件满足的情况下,会被转换成raw对象,如向对象保存值为字符串等
embstr编码是专门用来保存短字符串的一种优化编码的方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构,唯一不同的是embstr编码通过一次内存分配函数分配一块连续的空间,空间中包含redisObject和sdshdr两个结构。
优点:
- embstr创建字符串所需要分配内存次数从raw的两次变为一次
- 释放embstr编码的字符串对象只需要调一次内存释放,而raw需要释放两次
- 因为embstr编码的字符串对象的所有数据都是保存在一块连续的内存里,所以这种编码的字符串比起raw能够更好的利用缓存带来的优势
三、列表对象
列表对象编码可以是ziplist或者linkedlist
ziplist和linkedlist之间的转换:
当列表对象可以同时满足以下两个条件,列表使用ziplist,否则使用linkedlist:
- 列表保存的所有字符串元素长度都小于64字节
- 列表对象保存的元素数量小于512个
四、哈希对象
哈希对象可以是ziplist和hashtable
ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推到压缩列表的表尾,然后再将保存了值的压缩列表节点推到压缩列表的表尾。因此:
- 保存了同一键值对的两个节点总是挨在一起,保存键的节点在前,保存值的节点在后
- 先添加到哈希对象的键值对会被放在压缩列表的表头方向,而后来添加到哈希的键值对会被放在压缩列表的表尾
hashtable八编码的哈希对象使用字典作为底层实现:
- 字典的每个键都是一个字符串对象,对象中保存了键值对的键
- 字典的每个值都是一个字符串对象,对象中保存了键值对的值
当哈希对象可以同时满足以下两个条件,列表使用ziplist,否则使用hashtable:
编码转换:
- 哈希对象保存的所有键值对的键和值字符串长度都小于64字节
- 哈希对象保存的键值对数量小于512个
五、集合对象
集合对象的编码可以是intset或者hashtable
intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素被保存在整数集合里。
hashtable编码的集合对象使用字典作为底层实现,字典每个键都是一个字符串对象,每一个字符串包含了一个集合元素,而字典的值都为null
编码的转换:
当集合对象可以同时满足以下两个条件,对象使用intset编码,否则使用hashtable编码:
- 集合对象保存的所有元素都是整数值
- 集合对象保存的所有元素不超过512个
六、有序集合对象
有序集合对象可以是ziplist或者skiplist
ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨一起的压缩列表节点保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)。压缩列表从小到达进行排序
skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表
- 跳跃表作用是按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的object保存了元素的成员,而跳跃表节点的score保存了分值。
- 字典的作用是创建一个成员到分值的映射,字段每个键值对都保存了一个集合元素。有序集合
编码的转换:
当集合对象可以同时满足以下两个条件,对象使用ziplist编码,否则使用skiplist编码:
- 有序集合对象保存的所有元素成员长度都小于64字节
- 有序集合对象保存的所有元素成员数量小于128个
上面所说的默认值的情况,这个值可以根据配置文件的zset-max-ziplist-entries和zset-max-ziplist-value进行修改
六、内存回收
因为C语言不具备自动内存回收功能,所以redis在字节的对象系统中构建了一个引用计数技术实现的内存回收机制,通过这一个机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收
七、对象共享
对象共享好处:相同的值对象越多,对象共享机制就能节约更多的内存
在redis中,让多个键共享同一个值对象需要执行以下两个步骤:
- 数据库键的值指向一个现有值得对象
- 将被共享的值对象引用计数器加一
目前来说,redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了0-9999的所有整数值,当服务器需要这些值的时候,就会引用共享对象,而不是创建对象