Redis-数据结构-对象

对象

        redis没有直接使用SDS、链表、字典、压缩列表、整数集合等数据结构来实现    键值对数据库,而是基于这些数据结构创建了一个对象系统,包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象 这五种类型的对象。   

1、对象的类型与编码

        redis使用对象来表示数据库中的键和值,对象包含字符串(string)、列表(list)、哈希(hash)、集合(set)、有序集合(z-set)5中类型。当在redis数据库中创建一个键值对时,至少创建两个对象,一个用作键(键对象),另一个用作值(值对象)。

        好处是:1)可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化不同场景下的使用效率。2)对象系统实现了基于引用计数的内存回收机制。3)通过引用计数实现了对象共享机制,在适当的条件下通过共享同一对象来节约内存。4)redis对象带有访问时间记录信息(最后访问的时间戳),在启动maxmemory功能的情况下,较长时间未访问的键可能会优先被服务器删除

 结构

type值

 encoding值

2、字符串对象

字符串对象的编码可以是 int、raw、或者 embstr

        如果一个字符串对象保存的是整数值,并且可以用long类型表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里(将void*转换成long),并将字符串对象的编码设置为int;


        如果字符串对象保存的是一个字符串值,并且长度大于32字节,那么字符串对象将使用SDS来保存这个字符串值,并将编码设置为rew;

        如果字符串对象保存的是一个字符串值,并且长度小于等于32字节,那么字符串对象将使用embstr编码的SDS来保存之歌字符串值。

        embstr编码是专门用于保存短字符串的一种优化编码方式,和raw编码一样,都使用sdshdr结构来表示。但raw编码会调用两次内存分配分别创建redisObject结构和sdshdr结构,而embstr编码则动过调用一次内存分配来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构。

embstr编码字符串对象在执行命令时,产生的效果和raw编码的字符串对象是相同的。

embstr编码的好处:

        1)embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降为一次;

        2)释放embstr编码字符串对象只需要调用一次内存释放函数;

        3)embstr编码字符串对象所有数据保存在一块连续内存内,能更好地利用缓存的优势。

long double类型表示的数在Redis中作为字符串值来保存,如果要保存一个浮点数到字符串对象,需要先将浮点数转换成字符串值,然后再保存转换所得的字符串值。

3、列表对象

列表对象的编码可以是 ziplist linkedlist

ziplist编码使用压缩列表作为底层实现,么个压缩列表节点(entry)保存一个元素。

linkedlist编码使用双端链表作为底层实现,每个双端链表节点(node)都保存一个字符串对象,么个字符串对象保存一个元素。

linkedlist编码的列表对象在底层的双端列表结构中包含了多个字符串对象,字符串对象是Redis五种类型对象中唯一会被其他四种类型对象嵌套的对象。

使用ziplist编码需要同时满足以下2个条件,否则使用linkedlist编码:

1)列表对象保存的所有字符串元素的长度都小于64字节;

2)列表对象保存的元素数量小于512个;

4、哈希对象

哈希对象的编码可以是 ziplist 或者 hashtable

ziplist编码 使用压缩列表作为底层实现,每当有新的键值对加入到哈希对象时,先将保存了键的节点推入列表表尾,再讲保存了值的节点推入列表表尾。1)保存了同一键值对的连个节点紧挨在一起,保存键的节点在前,保存值的节点在后;2)先添加到哈希对象的键值对会被放在压缩列表的表头方向,后添加的键值对会被放在压缩列表的表尾方向。

hashtable编码 的哈希对象使用字典作为底层实现,每个键值对使用一个字典键值对保存:字典的键都是一个字符串对象,保存哈希对象的键;字典的值都是一个字符串对象,保存键值对的值。

编码转换

哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;

哈希对象保存的键值对数量小于512个;

同时满足以上2个条件使用ziplist编码;否则使用hashtable编码

5、集合对象

集合对象set的编码可以是 intset或者hashtable

intset编码的集合对象使用整数集合作为底层实现

hashtable编码的集合对象使用字典作为底层实现,字典的键保存集合元素,字典的值为null。


当集合对象同时满足以下两个条件时,使用intset编码

1)集合对象保存的所有元素都是整数值;

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

6、有序集合对象

有序集合 zset 编码可以是ziplist或者skiplist

ziplist编码 每个集合元素使用两个相连的节点来保存,第一个节点保存元素成员(member),第二个元素保存元素的分值(score)。压缩列表内的集合元素按照分值的从小到大排序。

skiplist编码 使用zset机构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。

zset结构中的zsl跳跃表按分值从小到大保存了所有集合元素,每个节点保存一个元素:跳跃表节点的object属性保存元素(member),跳跃表节点的score属性保存分值(score);zset结构中dict字典为有序集合创建了一个从成员到分值的映射,字典中每个键值对保存一个集合元素:字典的键保存元素成员(member),字典的值保存元素的分值(score),通过这个字典可以用O(1)复杂度查找给定成员的分值

当同时满足以下条件时,对象使用ziplist编码

1)由于集合保存的元素数量小于128个;

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

7、类型检查与命令多态

        7.1 类型检查

Redis中用于操作键的命令基本上分为两种类型:

1)可以对任何类型的键执行,如DEL、EXPIRE、RENAME、TYPE、OBJECT

2)只能UI特定类型的键执行

    SET、GET、APPEND、STRLEN命令只能对 string类型执行

    HDEL、HSET、HGET、HLEN命令只能对 hash类型执行

    RPUSH、LPOP、LINSERT、LLEN 命令只能对 list类型执行

    SADD、SPOP、SINTER、SCARD命令只能对 set类型执行

    ZADD、ZCARD、ZRANK、ZSCORE命令只能对 zset类型执行

为了确保只有指定类型的键可以执行某些特定的命令,在执行一个类型特定的命令之前,Redis会先检查输入键的类型是否正确,然后再决定是否执行给定的命令。

类型检查通过redisObject结构的type属性来实现。


7.2 多态命令

Redis除了根据值对象的类型判断键是否能够执行命令之外,还会根据值对象的编码方式,选择正确的命令实现代码来执行命令。

DEL、EXPIRE 等命令是基于类型(type)的多态,一个命令可以同时用于处理多种不同类型的键

LLEN、SET等命令是基于编码(encoding)的多态,一个命令可以同时用于处理多种不同的编码


8、内存回收

Redis在自己的对象系统中构建了一个引用计数技术实现的内存回收机制。通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。

对象的引用计数会随着对象的使用状态而不断变化:

1)在创建一个新对象时,引用计数的值被初始化为1;

2)当对象被一个新程序使用时,它的引用计数值+1;

3)当对象不再被一个程序使用时,它的引用计数值-1;

4)当对象的引用计数值为0时,对象所占用的内存会被释放。

9、对象共享

除了用于实现引用计数内存回收机制之外,对象的引用计数属性还带有共享的作用。

在Redis中,让多个键共享同一个值对象需要执行以下两个步骤:

1)将数据库键的值指针指向一个现有的值对象;

2)将共享的值对象的引用计数+1。

Redis会在初始化服务器时,创建一万个字符串对象,包含了从 0 到 9999 的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象。

共享对象不仅字符串键可以使用,嵌套了字符串对象的对象都可以使用这些共享对象。

Redis只对 0 到 9999 的字符串对象进行共享

10、对象空转时长

redisObject的 lru 属性记录了对象最后一次被命令程序访问的时间;

空转时长= 当前时间 - lru时间

OBJECT IDLETIME 命令在访问键的值对象时,不会修改值对象的lru属性。

你可能感兴趣的:(Redis-数据结构-对象)