redis源码阅读之intset

栏目: 数据库 · 发布时间: 5年前

内容简介:今天来讲讲redis当中set的一种实现形式intset,顾名思义,其应用场景只是在集合当中只包含整数值并且元素数量不多时,set才会采用的一种实现方式。其存储结构如下所示:其中三个宏定义表示的是encoding字段的可能取值,分别对应intset中的contents数组的三种不同的解析方式(16位整数,32位整数,64位整数)。

今天来讲讲 redis 当中set的一种实现形式intset,顾名思义,其应用场景只是在集合当中只包含整数值并且元素数量不多时,set才会采用的一种实现方式。

其存储结构如下所示:

点击( 此处 )折叠或打开

  1. typedef struct intset {
  2.     uint32_t encoding ; / / 编码规则,具体见下面三个宏定义
  3.     uint32_t length ; / / 所存元素个数
  4.     int8_t contents [ ] ; / / 元素存储位置
  5. } intset ;
  6. #define INTSET_ENC_INT16 ( sizeof ( int16_t ) )
  7. #define INTSET_ENC_INT32 ( sizeof ( int32_t ) )
  8. #define INTSET_ENC_INT64 ( sizeof ( int64_t ) )

其中三个宏定义表示的是encoding字段的可能取值,分别对应intset中的contents数组的三种不同的解析方式(16位整数,32位整数,64位整数)。 这里需要注意的是由于不同的机器本地 的endian可能 不同,redis使用了endianconv.h文件中的函数对这方面进行了统一。 在contents中存放的实际数字时按照从小到大的顺序排列好的,并且没有重复项。

整个intset里面比较好玩的就是插入元素了。由于有三种不同的编码方式,当插入的新元素所占的空间较大,大到目前intset自身的编码无法承载这一数据的时候,intset就会根据新插入的值得大小,将原有的inset的encoding变为需要的,其具体步骤如下:

  1. 根据插入的元素类型,适当扩展intset所占的空间
  2. 在维持低层数组存储数据不变的基础上,按照 从后到前的顺序(避免覆盖) 拷贝到新的位置上
  3. 将新添加的元素插入到intset当中

其中插入代码如下所示:

点击( 此处 )折叠或打开

  1. / * Insert an integer in the intset * /
  2. intset * intsetAdd ( intset * is , int64_t value , uint8_t * success ) {
  3.     uint8_t valenc = _intsetValueEncoding ( value ) ;
  4.     uint32_t pos ;
  5.      if ( success ) * success = 1 ;
  6.      / * Upgrade encoding if necessary . If we need to upgrade , we know that
  7.       * this value should be either appended ( if > 0 ) or prepended ( if < 0 ) ,
  8.       * because it lies outside the range of existing values . * /
  9.      if ( valenc > intrev32ifbe ( is - > encoding ) ) {
  10.          / * This always succeeds , so we don ' t need to curry * success . * /
  11.         return intsetUpgradeAndAdd ( is , value ) ;
  12.      } else {
  13.          / * Abort if the value is already present in the set .
  14.           * This call will populate "pos" with the right position to insert
  15.           * the value when it cannot be found . * /
  16.          if ( intsetSearch ( is , value , & pos ) ) {
  17.              if ( success ) * success = 0 ;
  18.             return is ;
  19.          }
  20.          is = intsetResize ( is , intrev32ifbe ( is - > length ) + 1 ) ;
  21.          if ( pos < intrev32ifbe ( is - > length ) ) intsetMoveTail ( is , pos , pos + 1 ) ;
  22.      }
  23.     _intsetSet ( is , pos , value ) ;
  24.      is - > length = intrev32ifbe ( intrev32ifbe ( is - > length ) + 1 ) ;
  25.     return is ;
  26. }

在这个函数当中,首先判断了插入值的编码,若应该分配较大的空间,直接调用update函数

点击( 此处 )折叠或打开

  1. / * Upgrades the intset to a larger encoding and inserts the given integer . * /
  2. static intset * intsetUpgradeAndAdd ( intset * is , int64_t value ) {
  3.     uint8_t curenc = intrev32ifbe ( is - > encoding ) ;
  4.     uint8_t newenc = _intsetValueEncoding ( value ) ;
  5.      int length = intrev32ifbe ( is - > length ) ;
  6.      int prepend = value < 0 ? 1 : 0 ;
  7.      / * First set new encoding and resize * /
  8.      is - > encoding = intrev32ifbe ( newenc ) ;
  9.      is = intsetResize ( is , intrev32ifbe ( is - > length ) + 1 ) ;
  10.      / * Upgrade back - to - front so we don ' t overwrite values .
  11.       * Note that the "prepend" variable is used to make sure we have an empty
  12.       * space at either the beginning or the end of the intset .  
  13.      * 通俗的将就是对新插入的元素,我们应该插在头部还是插在尾部 * /
  14.      while ( length - - )
  15.         _intsetSet ( is , length + prepend , _intsetGetEncoded ( is , length , curenc ) ) ;
  16.      / * Set the value at the beginning or the end . * /
  17.      if ( prepend )
  18.         _intsetSet ( is , 0 , value ) ;
  19.      else
  20.         _intsetSet ( is , intrev32ifbe ( is - > length ) , value ) ;
  21.      is - > length = intrev32ifbe ( intrev32ifbe ( is - > length ) + 1 ) ;
  22.     return is ;
  23. }

如果当前intset当中已经有该元素的话,直接返回。若没有,需要找到应该插入的位置,移动其后面的元素,再将该值插入到应该插入的位置。

删除操作的话,逻辑就是能否找到,若找到的话,就直接将后面的元素移动过来,直接覆盖即可

点击( 此处 )折叠或打开

  1. / * Delete integer from intset * /
  2. intset * intsetRemove ( intset * is , int64_t value , int * success ) {
  3.     uint8_t valenc = _intsetValueEncoding ( value ) ;
  4.     uint32_t pos ;
  5.      if ( success ) * success = 0 ;
  6.      if ( valenc < = intrev32ifbe ( is - > encoding ) & & intsetSearch ( is , value , & pos ) ) {
  7.         uint32_t len = intrev32ifbe ( is - > length ) ;
  8.          / * We know we can delete * /
  9.          if ( success ) * success = 1 ;
  10.          / * Overwrite value with tail and update length * /
  11.          if ( pos < ( len - 1 ) ) intsetMoveTail ( is , pos + 1 , pos ) ;
  12.          is = intsetResize ( is , len - 1 ) ;
  13.          is - > length = intrev32ifbe ( len - 1 ) ;
  14.      }
  15.     return is ;
  16. }

这里面需要提醒诸位的是:

删除元素的时候没有降级操作!!!个人猜测的原因为:如果删除一个元素就要从头到尾检查当前的encoding是否过大的话,就会造成不必要的消耗,还不如让其多占用点内存了~~~


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

妙手回春

妙手回春

(美)Steve Krug / 袁国忠 / 人民邮电出版社 / 2010-7 / 39.00元

本书是作者Steve Krug继畅销书《点石成金:访客至上的网页设计秘笈》(Don't Make Me Think)后推出的又一力作。多年来,人们就认识到网站可用性测试可以极大地改善产品质量,但鉴于正规的可用性测试流程复杂、费用高昂,很少人这样做。在本书中,作者详细阐述了一种简化的网站可用性测试方法,让任何人都能够尽早并频繁地对其网站、应用程序和其他产品进行可用性测试,从而将最严重的可用性问题消灭......一起来看看 《妙手回春》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具