一、对象的类型与编码
1.1 type(类型)
对于Redis数据库保存的键值对来说,键总是一 个字符串对象,而值则可以是字符串对象、列表对 象、哈希对象、集合对象或者有序集合对象的其中 一种。
字符串对象:string
INT
EMBSTR
RAW
列表对象:list
ZIPLIST
LINKEDLIST
哈希对象:hash
ZIPLIST
HT
集合对象:set
INTSET
HT
有序集合对象:zset
ZIPLIST
SKIPLIST
1.2 编码
二、字符串对象
字符串对象的编码可以是int、raw或者embstr。
情况如下:
1.对象是一个长度小于等于32字节字符串(embstr):
embstr编码将创建字符串对象需要进行一次内存分配且保存在一块连续的内存里。embstr编码是只读,如果想要修改数据需要转换成raw编码
2.对象是一个长度大于32字节字符串(raw):
raw编码将创建字符串对象需要进行一次内存分配
3.字符串对象保存的是整数值(int):
当保存一个字符串时编码将从int变为raw
embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一 样,都使用redisObject结构和sdshdr结构来表示字符串对象。
embstr编码的字符串 对象在执行命令时,产生的 效果和raw编码的字符串对 象执行命令时产生的效果是 相同的,但使用embstr编码的字符串对象来保存短字符串值有以下好处:
1.embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次。
2.释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的 字符串对象需要调用两次内存释放函数。
3..因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面,所以这 种编码的字符串对象比起raw编码的字符串对象能够更好地利用缓存带来的优势
2.1、编码的转换
int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换 为raw编码的字符串对象。
int转raw:
对于int编码的字符串对象来说,如果我们向对象执行了一些命令,使得这个对象保存的不再是整数值,而是一个字符串值,那么字符串对象的编码将从int变为raw。
embstr转raw:
另外,因为Redis没有为embstr编码的字符串对象编写任何相应的修改程序(只有 int编码的字符串对象和raw编码的字符串对象有这些程序),所以embstr编码的字符 串对象实际上是只读的。当我们对embstr编码的字符串对象执行任何修改命令时,程序 会先将对象的编码从embstr转换成raw,然后再执行修改命令。因为这个原因,embstr 编码的字符串对象在执行修改命令之后,总会变成一个raw编码的字符串对象
2.2、字符串命令的实现
三、列表对象
列表对象的编码可以是ziplist或者linkedlist。
ziplist: 每个压缩列表节点(entry)保 存了一个列表元素
linkedlist:每个双端链表节 点(node)都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素
3.1、编码转换
列表对象保存的所有字符串元素的长度都小于64字节;列表对象保存的元素数量小于512个;
不能满足这两个条件的列表对象需要使用 linkedlist 编码。
注意:
以上两个条件的上限值是可以修改的,具体请看配置文件中关于list-max-ziplist- value 选项和 list-max-ziplist-entries 选领的说明。
3.2、列表命令的实现
四、哈希对象
哈希对象的编码可以是ziplist或者hashtable。
zipList:
保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点 在后
先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对 象中的键值对会被放在压缩列表的表尾方向
hashtable:
字典的每个键都是一个字符串对象,对象中保存了键值对的键
字典的每个值都是一个字符串对象,对象中保存了键值对的值
4.1、编码转换
ziplist:
哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
哈希对象保存的键值对数量小于512个;不能满足这两个条件的哈希对象需要使用 hashtable 编码
这两个条件的上限值是可以修改的,具体请看配置文件中关于hash-max-ziplist- value 选项和 hash-max-ziplist-entries 选项的说明
4.2、哈希命令实现
5、集合对象
集合对象的编码可以是intset或者hashtable
intset:集合对象包含的所有元素都被保 存在整数集合里面
hashtable:字典的每个键都是一 个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL
5.1 编码的转换
intset:
集合对象保存的所有元素都是整数值
集合对象保存的元素数量不超过512个
注意:
第二个条件的上限值是可以修改的,具体请看配置文件中关于set-max-intset- entries选项的说明。
5.2 集合命令实现
6、有序集合
有序集合的编码可以是ziplist或者skiplist
ziplist:每个集合元素使用两个紧 挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素 则保存元素的分值(score );
skiplist:
使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表:
跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的 score属性则保存了元素的分值
zset结构中的diet字典为有序集合创建了一个从成员到分值的映射,字 典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,而字典的值则保存 了元素的分值
字典和跳跃表会共享元素的成员和分值
6.1 编码的转换
ziplist:
有序集合保存的元素数量小于128个
有序集合保存的所有元素成员的长度都小于64字节
注意
以上两个条件的上限值是可以修改的,具体请看配置文件中关于zset-max-ziplist- entries 选项和 zset-max-ziplist-value 选项的说明.
对于使用ziplist编码的有序集合对象来说,当使用ziplist编码所需的两个条 件中的任意一个不能被满足时,就会执行对象的编码转换操作,原本保存在压缩列表里 的所有集合元素都会被转移并保存到zset结构里面,对象的编码也会从ziplist变为 skiplist
6.2、有序集合的命令实现
七、类型的检查和命令的多态
Redis中用于操作键的命令基本上可以分为两种类型。
其中一种命令可以对任何类型的键执行,比如说DEL命令、EXPIRE命令、RENAME 命令、TYPE命令、OBJECT命令等。
而另一种命令只能对特定类型的键执行,比如说:
SET、GET、APPEND、STRLEN等命令只能对字符串键执行;
HDEL、HSET、HGET、HLEN等命令只能对哈希键执行;
RPUSH、LPOP、LINSERT、 LLEN等命令只能对列表键执行;
SADD、SPOP、SINTER、SCARD等命令只能对集合键执行;
ZADD、ZCARD、ZRANK、ZSCORE等命令只能对有序集合键执行;
7.1 类型检查的实现
为了确保只有指定类型的键可以执行某些特 定的命令,在执行一个类型特定的命令之前,Redis会先检查输人键的类型是否正确,然后 再决定是否执行给定的命令。
类型特定命令所进行的类型检査是通过redisObject结构的type属性来实现的:
在执行一个类型特定命令之前,服务器会先检查输入数据库键的值对象是否为执行命令所需的类型,如果是的话,服务器就对键执行指定的命令;
否则,服务器将拒绝执行命令,并向客户端返回一个类型错误。
7.2、多态命令的实现
Redis除了会根据值对象的类型来判断键是否能够执行指定命令之外,还会根据值对象 的编码方式,选择正确的命令实现代码来执行命令。
考虑这样一个情况,如果我们对一个键执行命令,那么服务器除了要确保 执行命令的是列表键之外,还需要根据键的值对象所使用的编码来选择正确的命令 实现:
如果列表对象的编码为ziplist,那么说明列表对象的实现为压缩列表,程序将使 用ziplistLen函数来返回列表的长度;
如果列表对象的编码为linkedlist,那么说明列表对象的实现为双端链表,程序 将使用listLength函数来返回双端链表的长度;
八、内存回收
引用计数法:
在创建一个新对象时,引用计数的值会被初始化为1
当对象被一个新程序使用时,它的引用计数值会被增一
当对象不再被一个程序使用时,它的引用计数值会被减一
当对象的引用计数值变为〇时,对象所占用的内存会被释放
对象共享:
将数据库键的值指针指向一个现有的值对象
将被共享的值对象的引用计数增一
Redis只对包含整数值的字符串对象进行共享
Redis会共享值为0到9999的字符串对象
对象的空转时长:
Iru属性:redisObject 结构包含的最后一个属性为Iru属性,该属性记录了对象最后一次被命令程序访问的时间
一般用于自动回收算法