redis源码阅读之intset

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

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


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

查看所有标签

猜你喜欢:

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

一个人的电商

一个人的电商

许晓辉 / 电子工业出版社 / 2015-5-1 / CNY 59.00

首次披露电商的运营与操盘内幕,徐小平、梁宁作序,雷军、陈彤、张向东、刘韧、王峰力荐! 这个时代在经历前所未有的转型甚至颠覆,任何行业都将与互联网无缝融合,成为“互联网+”。有很多写电商的书,大多都用浓墨重彩阐释互联网转型的必要性,而讲到如何落地实操则浅尝即止,令人心潮澎拜之后不知如何下手。于是有了这本既有方法论,更重视实操细节的书。 许晓辉,在知名电商公司凡客诚品做过高管,有海......一起来看看 《一个人的电商》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具