02 October 2015

Redis对象的结构:

| type | | encoding | | ptr(指针) |

Type类型:

类型常量 对象名称
REDIS_STRING | 字符串对象
REDIS_LIST | 列表对象
REDIS_HASH | 哈希对象
REDIS_SET | 集合对象
REDIS_ZSET | 有序集合对象

encoding

Encoding属性记录了对象所使用的编码,什么样的编码对应对象底层的数据结构。在redis中,encoding属性的值都都有对应的常量来表示。

ptr指针

对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的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 | 跳跃表和字典

Redis对象对应的编码

类型 编码 对象
REDIS_STRING | REDIS_ENCODING_INT | 使用整数值实现的字符串对象
REDIS_STRING | REDIS_ENCODING_EMBSTR | 使用embstr编码的字符串对象
REDIS_STRING | REDIS_ENCODING_RAW | 使用简单动态字符串实现的字符串对象
REDIS_LIST | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现的列表对象
REDIS_LIST |REDIS_ENCODING_LINKEDLIST | 使用双端列表实现的列表对象
REDIS_HASH |REDIS_ENCODING_HT | 使用字典实现的哈希对象
REDIS_HASH |REDIS_ENCODING_ZIPLIST |使用压缩列表实现的哈希对象
REDIS_SET | REDIS_ENCODING_INTSET |使用整数集合编码实现的集合对象
REDIS_SET | REDIS_ENCODING_HT |使用字典实现的集合对象
REDIS_ZSET | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现的有序集合对象
REDIS_ZSET | REDIS_ENCODING_SKIPLIST | 使用跳跃比实现的有序集合对象


1.字符串对象

(1).字符串对象的编码可以是int ,raw 或embstr。

如果字符串对象保存的值是一个整数值,并且这个整数值可以用long表示,那么redis的字符串对象的编码将使用REDIS_ENCODING_INT。

如果字符串对象保存的值是一个字符串值,并且字符串的长度小于等于32个字节,那么redis字符串对象的编码将设置为REDIS_ENCODING_EMBSTR。

如果字符串对象保存的值是一个字符串值,并且字符串的长度大于32个字节,那么redis字符串对象的编码将设置为REDIS_ENCODING_RAW。

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

(2).使用embstr编码的好处是:

A.  相对于raw编码,只需要调用一次内存分配函数。

B.  在释放embstr编码的字符串的时候,也只需要调用一次内存释放函数。

C.  因为分配的是连续的内存块,相对于raw编码对象能够好的利用缓存。

Ps : long double类型的值在redis里面也是用字符串对象保存的,redis的底层会先将浮点数转换成字符串值,然后再保持在字符串对象中。

在有必要的情况下,字符串值也会转换成浮点数值,执行操作完以后,再转换成字符串值保存在字符串对象中。

(3) .编码的转换:

Int 编码和embstr编码的字符串对象在一定的情况下会转换成raw编码的字符串对象。

Redis 没有为embstr编码的字符串编写任何相应的修改程序,所以embstr编码的字符串是只读的,如果对embstr字符串进行修改操作,就需要对embstr编码的字符串进行编码转换。


2. 列表对象

(1).列表对象的编码可以是ziplist和linkedlist。


(2).编码转换。

当列表对象可以满足两个条件时,列表对象使用ziplist编码:

A.  列表对象保存的所有字符串元素的长度都小于64字节。

B.  列表对象保存元素的数量小于512个。

Ps:这两种情况之外,列表对象都使用linkedlist编码。两个条件的上限值都是可以修改的,见配置文件中list-max-ziplist-value 和 list-max-ziplist-entries 选项的说明。



3. 哈希对象

(1).哈希对象的编码可以是ziplist 或者hashtable。


(2).编码转换。

当哈希对象可以同时满足以下两个条件时,使用ziplist编码:

A . 哈希对象保存的所有键值对的键和值的字符串都小于64字节。

B.哈希对象保存的键值对的数量小于512个。

Ps:以上两种情况不满足的话,就使用hashtable编码。键的长度和值得长度过长,键值对的数量过多,都会使用hashtable编码。



4.集合对象

(1).集合对象的编码可以是intset或hashtable。


(2)编码使用场景。

A.如果集合对象保存的所有元素都是整数值;

B.集合对象保存的元素数量不超过512个。

Ps:不能满足这两个条件的集合对象都需要使用hashtable编码。第二个条件的上限值是可以在配置文件中set-max-inset-entries 中修改的。



5.有序集合对象

(1).有序集合的编码可以是ziplist或skiplist 。


(2).编码转换。

使用ziplist的条件:

A. 有序集合保存的元素数量小于128个。

B.有序集合保存的所有元素成员的长度都小于64字节。

Ps :不能满足条件的有序集将使用skiplist编码,两个条件的上限值可以修改,zset-max-ziplist-entries 和zset-max-ziplist-value 。



Redis共享对象

整数类型的值可以共享对象,共享对象需要两个步骤:

1:将数据库的值指针指向一个现有的值对象。

2:将被共享的值对象的引用计数加1 。

ps :Redis会在初始化服务器时,创建1万个字符串对象,包含0~9999的所有整数值。


为什么redis不共享包含字符串的对象?

因为程序在判断一个共享对象时,需要判断是否跟目标对象完全相同,而一个共享对象保存的值越复杂,验证共享对象和目标对象是否相同的复杂度就越高,消耗的CPU时间也就越多。