redis-整数集合

来源:互联网 发布:广州多想网络 骗 编辑:程序博客网 时间:2024/06/08 16:37

redis- 整数集合

整数集合(intset)是集合键的底层实现之一。当一个集合只包括整数元素并且元素个数不多时,Redis将会使用整数集合来存储。

看下面例子,当集合s1只存储整数1、2、4时,底层采用intset来存储;往集合s1中添加字符串”hello”后,底层存储变为哈希表。

redis的集合数据结构(set)其实是利用哈希表实现的,哈希表的键就是集合的元素,哈希表的值都是NULL。

127.0.0.1:6379> sadd s1 1 2 4(integer) 3127.0.0.1:6379> object encoding s1"intset"127.0.0.1:6379> sadd s1 "hello"(integer) 1127.0.0.1:6379> object encoding s1"hashtable"

以下片断是t_set.c的部分代码,从注释中可以看到,当值是一个整数值时返回intset,否则返回一个hashtable。

/* Factory method to return a set that *can* hold "value". When the object has * an integer-encodable value, an intset will be returned. Otherwise a regular * hash table. */robj *setTypeCreate(robj *value) {    if (isObjectRepresentableAsLongLong(value,NULL) == C_OK)        return createIntsetObject();    return createSetObject();}

整数集合的实现

redis整数集合可以存储int16_t、int32_t或者int64_t类型的整数值,并且保证集合中不存在重复元素。

redis的整数集合定义如下:

typedef struct intset {  //编码方式  uint32_t encoding;  //集合包含的元素数量  uint32_t length;  //保存元素的数组  int8_t contents[];} intset;
  • encoding:整数集合保存的数据类型,encoding属性取值范围包括:
    • INTSET_ENC_INT16,此时contents数组元素值的范围为[-2^15^, 2^15^-1]
    • INTSET_ENC_INT32,此时contents数组元素值的范围为[-2^31^, 2^31^-1]
    • INTSET_ENC_INT64,此时contents数组元素值的范围为[-2^63^, 2^63^-1]
  • length:整数集合包含的元素数量,也就是contents数组的大小
  • contents:实际存储元素的数组,虽然contents数组的类型声明为int8_t,但是实际使用是根据encoding来决定数组存储的整数类型的。另外,contents中的元素是根据元素大小从小到大排列的,且contents中不存在任何重复项。

这里写图片描述

图1

图1是一个包括6个元素的整数集合,其中元素是int16_t类型的。

升级

对于图1的例子,如果此时往该整数集合插入一个int32_t类型的值,比如插入2010483000,显然原来的int16_t类型存储不了该值,此时需要对整数集合进行升级。

升级步骤为:

  1. 根据新元素的类型,扩展底层存储空间的大小,并为新添加的元素预留存储空间。
  2. 调整原有数据的数据类型为新的数据类型,预留每个元素的空间。
  3. 将新添加的元素放到整数集合的正确位置。

例如对于图1,此时要插入2010483000,需要将原有的数据类型升级为int32_t

  1. 首先计算需要的空间大小。元素个数有7个,每个大小为32位,总共有7x32=224位,空间分配如图2。
    这里写图片描述
    图2

从图2可以看出,新分配的空间位数为96-223位。

  1. 元素198排在第6位,所以它的最终位置为160-191位;

    元素89排在第5位,所以它的最终位置为128-159位;

    元素45排在第4位,所以它的最终位置为96-127位;

    元素23排第3位,所以它的最终位置为64-95位;

    元素-456排在第2位,所以它的最终位置为32-63位;

    元素-1234排在第1位,所以它的最终位置为0-31位;

    最终如图3所示

    这里写图片描述

图3

新插入的元素放到第7位的空间,最终如图4所示。

这里写图片描述

图4

最后程序将encoding属性值改为INTSET_ENC_INT32,将length属性值改为7。

注意:如果新添加的元素引起了整数集合的升级,那么这个元素要么是比原来所有的元素都大,要么比原来所有的元素都小(负数)。因此新添加的元素要么位于数组contents索引0处,要么位于数组末尾。

为何要升级?

redis这样设计可以节省内存的使用,只有需要的时候才会去重新分配更多的内存,提高了内存的使用效率。

降级

redis不支持降级,当整数类型升级后不可以降级。比如整数集合只有一个类型为int32_t的元素,其他的元素用int16_t类型就可以存储,此时将该元素删除后,整数集合并不会降级为int16_t,而还是维持int32_t类型的。

注意点

  1. 整数集合底层是用数组存储的,所以添加和移除的时间复杂度为O(n)
  2. 整数集合底层存储是有序的,所以查找操作可以用二分查找,时间复杂度为O(log^N^)

参考:

  1. Redis设计与实现. 黄健宏著.
原创粉丝点击