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是否过大的话,就会造成不必要的消耗,还不如让其多占用点内存了~~~


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

查看所有标签

猜你喜欢:

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

Rework

Rework

Jason Fried、David Heinemeier Hansson / Crown Business / 2010-3-9 / USD 22.00

"Jason Fried and David Hansson follow their own advice in REWORK, laying bare the surprising philosophies at the core of 37signals' success and inspiring us to put them into practice. There's no jarg......一起来看看 《Rework》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具