redis系列之数据结构

redis五种数据结构的介绍

redis五种数据结构的比较

redis五种数据结构的使用

redis五种数据结构的介绍

Redis可以存储键五种数据结构,这五种数据结构分别是STRING(字符串),LIST(列表),SET(集合),HASH(散列),ZSET(有序集合)。下面我们会对这五种数据结构进行介绍。不过在介绍着五种数据类型之前,先简单说一下redis的key键。Rediskey值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如”foo”的简单字符串到一个JPEG文件的内容都可以。空字符串也是有效key值。

.字符串(STRING)

String是redis最简单的key-value结构类型,value值不仅可以是String,也可以是数字(当数字类型是Long的时候,encoding就是整型,其他都存储在sdshdr当做字符串)。你可以存放任何种类的字符串,包括二进制数据,例如你可以在一个键下保存一副jpeg图片。值的长度不能超过GB。使用String类型,可以完全实现目前Memcached的功能,并且效率更高。还可以享受Redis的定时持久化(可以选择RDB模式或者AOP模式),操作日志及Replication等功能。

redis中的字符串并没有直接使用C语言传统的字符串来表示,而是构建了一种简单动态字符串的类型,简称SDS,并将其作为redis默认的字符串来表示。在redis中,C字符串只是用来做字符串的字面量,当用来作为可以修改的字符串的时候使用的是SDS。

SDS的结构为三部分,charbuf字节数组,用来保存字符串;intlen用来记录buf数组中已经使用字节的数;intfree记录buf数组中未使用的字节数。我们用SDS和C字符串来做一下比较。我们知道,C字符串的长度是N+,最后一个元素总是空字符‘\n’。因为C字符串并没有记录字符串的长度,所以为了获取一个字符串的长度,程序必须遍历整个字符串,对遇到的每个字符进行计算,直到遇到空字符,在C字符串中只能有一个空字符,它会以遇到的第一个空字符来判断这个字符是否结束。由于C字符串底层永远是一个N+的数组,所以,每次对字符串进行增加或者减少的操作的时候,程序总要对保存这个字符串的数组进行一次内存重分配的操作。如果不这么做,在进行字符串拼接或者增加其长度的时候,有可能会导致缓冲区溢出,当减少其长度的时候,可能会导致内存泄漏。但是SDS为了避免这些,做了调整。SDS中有专门记录字符长度的len值和记录空闲长度的free值。程序只需直接访问SDS的len值就可以知道字符串的长度,当程序修改对SDS进行修改的时候,会先检测SDS字符串的空间是否满足修改所需要的要求,如果不满足,会自动的将SDS的空间扩展到执行修改所需要的大小。但是一旦涉及到扩展空间,就会出现内存重新分配的操作,redis的SDS为了避免这样,通过未使用空间,实现了空间预分配和惰性空间释放两种优化策略。

所谓空间预分配,当对SDS进行修改的时候,并且需要对SDS空间进行扩展的时候,程序不仅会为SDS分配修改所需要的空间,还会为SDS分配额外的未使用空间。其分配策略是如下定义的:如果对SDS修改后的长度小于MB,那么程序分配和len属性同样大小的未使用空间;如果对SDS修改后的长度大于等于MB,那么程序会分配MB的未使用空间。通过空间预分配策略,redis可以减少连续执行字符串增长操作所需要的内存重分配次数。

注意:无论是哪种情况,修改后的buf数组的实际长度最后都应该加上一字节。(可以想想为什么)

所谓惰性空间释放,就是当需要缩短SDS保存的字符串的时候,程序并不立即使用内存重新分配来回收缩短后多出来的字节,而是使用free属性将这些字节的数量记录下来,并等来将来使用。当然,当我们真正需要释放SDS的未使用空间的时候,可以通过提供的api释放。

注意:redis并不是用SDS的buf数组来保存字符的,而是来保存一系列二进制数据。

.列表(LIST)

要说清楚redis的列表数据类型,一般意义上讲,列表就是有序元素的序列。但是现在有很多地方对list的使用并不恰当,比如PythonLists就名不副实,名为Linkedlist,其实是数组,用数组实现lIst和用LinkedList实现list,在属性方面大不相同。

RedisList基于Linkedlist实现,Redis对链表(Linkedlist)的支持使得它在键值存储的世界中独树一帜,这意味着即使在一个list中有数百万个元素,在头部添加一个元素和在尾部添加一个元素,其时间复杂度也是常数级别的。在十个元素的list的头部添加一个元素和在十万个元素的list头部添加一个元素的速度相同。那么有优点就有缺点,我们知道如果用数组实现list,我们可以通过索引快速的访问list中的元素,但是如果用Linkedlist实现list就没有那么快了。RedisLists用linkedlist实现的原因是:对于数据库系统来说,至关重要的特性是:能非常快的在很大的列表上添加元素。另一个重要因素是,正如你将要看到的:Redislists能在常数时间取得常数长度。

Redis对外暴露的list数据类型,它底层实现所依赖的内部数据结构就是quicklist。在quicklist.c文件头部的注释中,是这样解释quicklist的:Adoublylinkedlistofziplists,一个ziplists的双向列表。双向链表是由多个节点(Node)组成的,quicklist中的每个节点都是一个ziplist,我们知道双向链表虽然利于在它的两端进行push和pop,但是由于它在每个节点上除了要保存数据之外还要额外的保存两个指针,其次双向链表的各个节点都是相互独立的内存块,地址不连续,节点多了容易产生内存碎片,所以它的内存消耗比较大。然而ziplist由于是一个连续的内存,所以存储效率很高,但是它不利于修改操作,每次数据变动都会引起一次内存的realloc,特别是在ziplist长度很长的时候,一次数据变动,一次realloc可能就会大批量数据,进一步降低性能。redis将这两点结合起来寻找一个平衡点。我们来分析一下,每个quicklist节点上的ziplist越短,内存碎片就越多,就有可能在内存中行成很多不能被利用的小碎片,从而降低存储效率。我们极端的假设一下,每个quicklist节点上的ziplist只有一个数据项,这样就变成一个普通的双向链表了。反过来说,如果每个quicklist节点上的ziplist越长,则为ziplist分配大块连续内存空间的难度就越大,如果这些空间没有被利用,就会在内存中形成很多小块的空闲空间,但却找不到一块足够的空闲空间分配给ziplist,这样同样会降低存储效率。这种情况的极端是整个quicklist只有一个节点,所有的数据项都分配在这仅有的一个节点的ziplist里面。这其实蜕化成一个ziplist了。可见,一个quicklist节点上的ziplist要保持一个合理的长度。那到底多长合理呢?这可能取决于具体应用场景。实际上,Redis提供了一个配置参数list-max-ziplist-size,就是为了让使用者可以来根据自己的情况进行调整。它可以取正值,也可以取负值。当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-到-5这五个值,每个值含义如下:

-5:每个quicklist节点上的ziplist大小不能超过64Kb。(注:kb=04bytes)-4:每个quicklist节点上的ziplist大小不能超过3Kb。-3:每个quicklist节点上的ziplist大小不能超过6Kb。-:每个quicklist节点上的ziplist大小不能超过8Kb。(-是Redis给出的默认值)-:每个quicklist节点上的ziplist大小不能超过4Kb。

另外,list的设计目标是能够用来存储很长的数据列表的。当列表很长的时候,最容易被访问的很可能是两端的数据,中间的数据被访问的频率比较低(访问起来性能也很低)。如果应用场景符合这个特点,那么list还提供了一个选项,能够把中间的数据节点进行压缩,从而进一步节省内存空间。Redis的配置参数list-







































白癜风患者同享健康礼
白癜风治好



转载请注明:http://www.92nongye.com/xxmb/204619505.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了