码迷,mamicode.com
首页 > 其他好文 > 详细

Redis短结构

时间:2021-04-02 13:13:35      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:序列   red   而在   简单   方便   分片   性能测试   选项说明   多个   

  本章将介绍3种非常有价值的降低Redis内存占用的方法。降低Redis的内存占用有助于减少创建快照和加载快照所需的时间、提升载入AOF文件和重写AOF文件时的效率、缩短从服务器进行同步所需的时间,并且能让Redis存储更多的数据而无需添加额外的硬件。

  本章首先会介绍如何使用Redis的短数据结构来更高效地表示数据。接着会介绍如何使用分片技术,将一些体积较大的结构分割为多个体积较小的结构。最后介绍如何将固定长度的数据打包存储到字符串键里面,从而进一步地降低内存占用。

  原书作者曾经通过同时使用本章介绍的这几种技术,成功地将分布在3台服务器上的70多GB数据缩小至3GB,并且只使用了 1台服务器进行存储。因为本章介绍的优化技术同样可以应用于本书之前介绍过的某些问题,所以本章在介绍各项优化技术的过程中,会在合适的时候提示如何将这些技术应用到之前介绍过的问题上。

 

1. 短结构

  Redis为列表、集合、散列和有序集合提供了一组配置选项,这些选项可以让Redis以更节约空间的方式存储长度较短的结构(后面简称“短结构”)。本节将对相关的配置选项进行介绍讲解如何验证这些配置选项的优化效果,并说明使用短结构带来的一些缺点。

  在列表、散列和有序集合的长度较短或者体积较小的时候,Redis可以选择使用一种名为压缩列表(ziplist)的紧凑存储方式来存储这些结构。压缩列表是列表、散列和有序集合这3种不同类型的对象的一种非结构化(unstructured)表示:与Redis在通常情况下使双链表表示列表、使用散列表表示散列、使用散列表加上跳跃表(skiplist)表示有序集合的做法不同,压缩列表会以序列化的方式存储数据,这些序列化数据每次被读取的时候都要进行解码,每次被写入的时候也要进行局部的重新编码,并且可能需要对内存里面的数据进行移动。

 

1.1 压缩列表显示

  为了了解压缩列表比其他数据结构更为节约内存的原因,我们需要对使用压缩列表的几种结构当中,最为简单的列表结构进行观察。在典型的双向链表(doubly linked list)里面,链表包含的每个值都会由一个节点(node )表示,每个节点都会带有指向链表中前一个节点和后一个节点的指针,以及一个指向节点包含的字符串值的指针。每个节点包含的字符串值都会分为3个部分进行存储:第一部分存储的是字符串的长度,第二部分存储的是字符串值中剩余可用的字节数量,而最后一部分存储的则是以空字符结尾的字符串本身。图9-1展示了一个比较长的双向链表的其中一部分,通过这个图可以看到"。ne"、"two"、"ten"这3个字符串是如何存储在双向链表里面的。

技术图片

  为了让图片保持简洁,图9-1省略了链表的某些细节。图中展示的3个3字符长的字符串,每个都需要空间来存储3个指针、2个整数(一个是字符串的长度,另一个是字符串值的剩余可用空间)、字符串本身以及一个额外的字节。在32位平台上,每存储一个这样的3字节长的字符串,就需要付出21字节的额外开销(overhead),而这还只是保守的估计值,实际的额外开销还会更多一些。

  另一方面,压缩列表是由节点组成的序列(sequence),每个节点都由两个长度值和一个字符串组成。第一个长度值记录的是前一个节点的长度,这个长度值将被用于对压缩列表进行从后向前的遍历,第二个长度值记录了当前节点的长度,而位于节点最后的则是被存储的字符串值。尽管压缩列表节点的长度值在实际中还有一些其他的含义,但是对于我们例子中的“one”、“two”、“ten” 这3个3字节长的字符串来说,它们每个的长度都可以用1字节来存储,所以在使用压缩列表存储这3个字符串的时候,每个节点只会有2字节的额外开销。通过避免存储额外的指针和元数据,使用压缩列表可以将存储示例中的3个字符串所需的额外开销从原来的21字节降低至2字节。

  下面就让我们来看看,如何使用紧凑的压缩列表编码。

 

  • 使用压缩列表编码

  为了确保压缩列表只会在有需要降低内存占用的情况下使用,Redis引入了代码清单9-1展示的配置选项,这些选项决定了列表、散列和有序集合会在什么情况下使用压缩列表表示。

技术图片

  列表、散列和有序集合的基本配置选项都很相似,它们都由-max-ziplist-entries选项和-max-ziplist-value选项组成,并且这3组选项的语义也基本相同:entries选项说明列表、散列和有序集合在被编码为压缩列表的情况下,允许包含的最大元素数量;而value选项则说明了压缩列表每个节点的最大体积是多少个字节。当这些选项设置的限制条件中的任意一个被突破的时候,Redis就会将相应的列表、散列或是有序集合从压缩列表编码转换为其他结构,而内存占用也会因此而增加。

技术图片

  通过使用新介绍的DEBUG OBJECT命令,我们可以很方便地了解一个对象是否被存储成了压缩列表,这对于减少内存占用非常有好处。

  跟列表、散列和有序集合不同,集合并没有使用压缩列表表示,而是使用了另外一种具有不同语义和限制的紧凑表示,接下来的一节就会对这种表示进行介绍。

 

1.2 集合的整数集合编码

  跟列表、散列和有序集合一样,体积较小的集合也有自己的紧凑表示:如果整数包含的所有成员都可以被解释为十进制整数,而这些整数又处于平台的有符号整数范围之内,并且集合成员的数量又足够少的话(具体的限制大小稍后就会说明),那么Redis就会以有序整数数组的方式存储集合,这种存储方式又被称为整数集合(insert)。

  以有序数组的方式存储集合不仅可以降低内存消耗,还可以提升所有标准集合操作的执行速度。那么一个集合要符合什么条件才能被存储为整数集合呢?代码清单9-3展示了定义整数集合最大元素数量的配置选项。

技术图片

  只要集合存储的整数数量没有超过配置设定的大小,Redis就会使用整数集合表示以减少数据的体积。代码清单9-4展示了当整数集合包含的元素数量超过配置选项设定的限制时,集合发生的一系列变化。

技术图片

  文章开头的简介部分曾经提到过,对一个压缩列表表示的对象的其中一部分进行读取或者更新,可能会需要对整个压缩列表进行解码,甚至还需要对内存里面的数据进行移动,因此读写一个长度较大的压缩列表可能会给性能带来负面的影响。使用整数集合编码的集合结构也有类似的问题,不过整数集合的问题并非来源于编码和解码数据,而在于它在执行插入操作或者删除操作时需要对数据进行移动。在接下来的一节中,我们将对长度较大的压缩列表在执行操作时产生的性能问题进行研究。

 

1.3 长压缩列表和大整数集合带来的性能问题

  当一个结构突破了用户为压缩列表或者整数集合设置的限制条件时,Redis就会自动将它转换为更为典型的底层结构类型。这样做的主要原因在于,随着紧凑结构的体积变得越来越大,操作这些结构的速度也会变得越来越慢。

  为了直接观察这个问题是如何发生的,我们首先需要把list-max-ziplist-entries选项的值设置为110000。这个值比实际中应用的值要大很多,但这有助于凸显我们想要发现的问题。在修改配置选项并重新启动Redis之后,我们将对Redis进行性能测试,以此来考察列表在使用长度较大的压缩列表编码时,性能问题是如何出现的。

  为了测试列表在使用长度较大的压缩列表作为编码时的性能表现,我们需要用到代码清单9-5展示的测试函数。这个函数首先会创建一个列表,并将指定数量的节点添加到列表里面,然后反复地调用RPOPLPUSH命令,将元素从列表的右端移动到左端,以此来计算列表在使用长度较大的压缩列表作为编码时,执行复杂命令时的性能下界。

  正如之前所说,long_ziplist_performance()函数会创建给定长度的列表,然后在流水线里面对列表执行指定数量的RPOPLPUSH命令调用。通过将RPOPLPUSH的调用次数除以执行这些调用花费的时间,程序可以计算出列表在使用给定长度的压缩列表作为编码时,每秒能够执行的操作数量。代码清单9-6展示了在列表长度逐渐增加的情况下,各个long_ziplist_performance()调用的执行结果,这些结果清晰地展示了列表的操作效率是如何随着压缩列表长度的增加而下降的。

技术图片

  初看上去,即使压缩列表的元素数量上升至好几千,测试得出的性能似乎也并不是太坏。但是别忘了这只是执行单个操作时的成绩,而这个操作所做的只不过是取出列表右端的元素然后将它推入列表的左端。尽管压缩列表在执行插入操作时需要移动所有元素的做法导致了性能下降.但压缩列表查找左端和右端的速度并不慢.更别说这个测试还充分地利用了CPU缓存。但是当Redis需要像自动补全例子一样,扫描整个列表以查找某个特定值的时候,又或者需要获取和更新散列的不同域(field) 的时候,Redis就会需要解码很多单独的节点,而CPU缓存的作用也会因此而受到影响:从数据上看,假如我们将long_ziplist_performance()函数中的RPOPLPUSH命令调用改为LINDEX命令调用,并使用LINDEX命令去获取位于列表中间的元素,那么当列表的元素数量超过5000个时,函数的性能将只有之前调用RPOPLPUSH命令时的一半,有兴趣的读者可以自己亲手去验证这一点。

  只要将压缩列表的长度限制在500?2000个元素之内,并将每个元素的体积限制在128字节或以下,那么压缩列表的性能就会处于合理范围之内。笔者的做法是将压缩列表的长度限制在1024个元素之内,并且每个元素的体积不能超过64字节,对于大多数散列应用来说,这种配置可以同时兼顾低内存占用和高性能这两方面优点。

  当为示例以外的其他问题开发解决方案的时候,请时刻记住,减少列表、集合、散列和有序集合的体积可以减少内存占用,并且能够帮助读者把Redis应用到解决更多不同的问题上面。

技术图片

 

 

Redis短结构

标签:序列   red   而在   简单   方便   分片   性能测试   选项说明   多个   

原文地址:https://www.cnblogs.com/lizexiong/p/14607761.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!