Redis(三)--- Redis的五大数据类型的底层实现

一、简介

Redis的五大数据类型也称五大数据对象;前面介绍过6大数据结构,Redis并无直接使用这些结构来实现键值对数据库,而是使用这些结构构建了一个对象系统redisObject;这个对象系统包含了五大数据对象,字符串对象(string)、列表对象(list)、哈希对象(hash)、集合(set)对象和有序集合对象(zset);而这五大对象的底层数据编码能够用命令OBJECT ENCODING来进行查看。java

redisObject结构redis

1 typedef struct redisObject { 2     // 类型
3     unsigned type:4; 4     // 编码
5     unsigned encoding:4; 6     // 指向底层实现数据结构的指针
7     void *ptr; 8     // ...
9 } robj;

redis是以键值对存储数据的,因此对象又分为键对象和值对象,即存储一个key-value键值对会建立两个对象,键对象和值对象。数据库

键对象老是一个字符串对象,而值对象能够是五大对象中的任意一种。缓存

    • type属性存储的是对象的类型,也就是咱们说的 string、list、hash、set、zset中的一种,可使用命令 TYPE key 来查看。
    • encoding属性记录了队形所使用的编码,即这个对象底层使用哪一种数据结构实现。

表中列出了底层编码常量及对应的OBJECT ENCODING 命令的输出,前三项都是字符串结构网络

咱们在存入key-value键值对时并不会指定对象的encoding,而是Redis会根据不统的使用场景来为一个对象设置不一样的编码,能够达到节约内存、加快访问速度等目的。数据结构

 

二、字符串对象(string)

字符串对象底层数据结构实现为简单动态字符串(SDS)和直接存储,但其编码方式能够是int、raw或者embstr,区别在于内存结构的不一样大数据

(1)int编码优化

字符串保存的是整数值,而且这个正式能够用long类型来表示,那么其就会直接保存在redisObject的ptr属性里,并将编码设置为int,如图:编码

 

(2)raw编码spa

 字符串保存的大于32字节的字符串值,则使用简单动态字符串(SDS)结构,并将编码设置为raw,此时内存结构与SDS结构一致,内存分配次数为两次,建立redisObject对象和sdshdr结构,如图:

(3)embstr编码

 字符串保存的小于等于32字节的字符串值,使用的也是简单的动态字符串(SDS结构),可是内存结构作了优化,用于保存顿消的字符串;内存分配也只须要一次就可完成,分配一块连续的空间便可,如图:

 

 字符串对象总结:

    • 在Redis中,存储long、double类型的浮点数是先转换为字符串再进行存储的。
    • raw与embstr编码效果是相同的,不一样在于内存分配与释放,raw两次,embstr一次。
    • embstr内存块连续,能更好的利用缓存在来的优点
    • int编码和embstr编码若是作追加字符串等操做,知足条件下会被转换为raw编码;embstr编码的对象是只读的,一旦修改会先转码到raw。

三、列表对象(list)

列表对象的编码能够是ziplist和linkedlist之一。

(1) ziplist编码

ziplist编码的哈希随想底层实现是压缩列表,每一个压缩里列表节点保存了一个列表元素。

(2)linkedlist编码

linkedlist编码底层采用双端链表实现,每一个双端链表节点都保存了一个字符串对象,在每一个字符串对象内保存了一个列表元素。

列表对象编码转换:

    • 列表对象使用ziplist编码须要知足两个条件:一是全部字符串长度都小于64字节,二是元素数量小于512,不知足任意一个都会使用linkedlist编码。
    • 两个条件的数字能够在Redis的配置文件中修改,list-max-ziplist-value选项和list-max-ziplist-entries选项。
    • 图中StringObject就是上一节讲到的字符串对象,字符串对象是惟一个在五大对象中做为嵌套对象使用的。

 

四、哈希对象(hash)

哈希对象的编码能够是ziplist和hashtable之一。

(1)ziplist编码

ziplist编码的哈希对象底层实现是压缩列表,在ziplist编码的哈希对象中,key-value键值对是以紧密相连的方式放入压缩链表的,先把key放入表尾,再放入value;键值对老是向表尾添加。

(2)hashtable编码

hashtable编码的哈希对象底层实现是字典,哈希对象中的每一个key-value对都使用一个字典键值对来保存。

字典键值对便是,字典的键和值都是字符串对象,字典的键保存key-value的key,字典的值保存key-value的value。

哈希对象编码转换:

    • 哈希对象使用ziplist编码须要知足两个条件:一是全部键值对的键和值的字符串长度都小于64字节;二是键值对数量小于512个;不知足任意一个都使用hashtable编码。
    • 以上两个条件能够在Reids配置文件中修改hash-max-ziplist-value选项和hash-max-ziplist-entries选项。

 

五、集合对象(set)

集合对象的编码能够是intset和hashtable之一。

(1)intset编码

intset编码的集合对象底层实现是整数集合,全部元素都保存在整数集合中。

(2)hashtable编码

hashtable编码的集合对象底层实现是字典,字典的每一个键都是一个字符串对象,保存一个集合元素,不一样的是字典的值都是NULL;能够参考java中的hashset结构。

集合对象编码转换:

    • 集合对象使用intset编码须要知足两个条件:一是全部元素都是整数值;二是元素个数小于等于512个;不知足任意一条都将使用hashtable编码。
    • 以上第二个条件能够在Redis配置文件中修改et-max-intset-entries选项。

 

 六、有序集合对象(zset)

有序集合的编码能够是ziplist和skiplist之一。

(1)ziplist编码 

ziplist编码的有序集合对象底层实现是压缩列表,其结构与哈希对象相似,不一样的是两个紧密相连的压缩列表节点,第一个保存元素的成员,第二个保存元素的分值,并且分值小的靠近表头,大的靠近表尾。

(2)skiplist编码

skiplist编码的有序集合对象底层实现是跳跃表和字典两种;

每一个跳跃表节点都保存一个集合元素,并按分值从小到大排列;节点的object属性保存了元素的成员,score属性保存分值;

字典的每一个键值对保存一个集合元素,字典的键保存元素的成员,字典的值保存分值。

为什么skiplist编码要同时使用跳跃表和字典实现?

    • 跳跃表优势是有序,可是查询分值复杂度为O(logn);字典查询分值复杂度为O(1) ,可是无序,因此结合连个结构的有点进行实现。
    • 虽然采用两个结构可是集合的元素成员和分值是共享的,两种结构经过指针指向同一地址,不会浪费内存。

有序集合编码转换:

  • 有序集合对象使用ziplist编码须要知足两个条件:一是全部元素长度小于64字节;二是元素个数小于128个;不知足任意一条件将使用skiplist编码。
  • 以上两个条件能够在Redis配置文件中修改zset-max-ziplist-entries选项和zset-max-ziplist-value选项。

 

七、总结

在Redis的五大数据对象中,string对象是惟一个能够被其余四种数据对象做为内嵌对象的;

列表(list)、哈希(hash)、集合(set)、有序集合(zset)底层实现都用到了压缩列表结构,而且使用压缩列表结构的条件都是在元素个数比较少、字节长度较短的状况下;

四种数据对象使用压缩列表的优势:

(1)节约内存,减小内存开销,Redis是内存型数据库,因此必定状况下减小内存开销是很是有必要的。

(2)减小内存碎片,压缩列表的内存块是连续的,并分配内存的次数一次便可。

(3)压缩列表的新增、删除、查找操做的平均时间复杂度是O(N),在N再必定的范围内,这个时间几乎是能够忽略的,而且N的上限值是能够配置的。

(4)四种数据对象都有两种编码结构,灵活性增长。

 

参考:

《Redis设计与实现》黄健宏著,网上对Redis的详解等

 

此博客为笔者使用redis好久以后,参考网络上各种文章总结性书写,原创手打,若有错误欢迎指正。

相关文章
相关标签/搜索