一.概述

  整数集合(intset)是集合键的底层实现之一, 当一个集合只包含整数值元素,并且这个集合元素数量不多时, Redis就会使用整数集合作为集合键的底层实现。下面创建一个只包含5个元素的集合键,并且集合中所有元素都是整数值,那么这个集合键的底层实现就会是整数集合。 接着添加非整数值,集合键的底层实现就会是hashtable。

127.0.0.1:> sadd numbers
(integer)
127.0.0.1:> object encoding numbers
"intset"
127.0.0.1:> sadd numbers 'one'
(integer)
127.0.0.1:> object encoding numbers
"hashtable"

二. 整数集合实现

  整数集合是Redis用于保存整数值的集合抽象数据结构,它可以保存类型为int16_t, int32_t, int64_t的整数值,并且保证集合中不会出现重复元素。数据集合定义如下:

// 每个intset.h/intset结构表示一个整数集合
typedef struct intset
{
//编码方式
uint32_t encoding;
//集合包含的元素数量
uint32_t length;
//保存元素的数组
int8_t contents[];
}intset;

  (1) contents数组是整数集合的底层实现,整数集合的每个元素都是contents数组的一个数组项(item),各个项在数组中按值从小到大有序排列,并且数组中不包含重复项。如下面脚本:

127.0.0.1:> sadd record
(integer)
127.0.0.1:> smembers record
) ""
) ""
) ""
) ""
) ""

  (2) length属性记录了整数集合包含的元素数量,也即是contents数组的长度。虽然contents属性声明为int8_t类型的数组,但实现上contents数组并不保存任何int8_t类型的值,contents数组的真正类型取决于encoding属性的值。

  a. 如果encoding 属性的值为intset_enc_int16,那么contents就是一个int16_t类型的数组,数组里的每个项都是一个int16_t类型的整数值(范围在 -32768 ~ 32767)。如下图encoding属性的值有5个整数型,根据这些整数值得出encoding为int16_t类型。

  b. 如果encoding属性的值为intset_enc_int32, 那么数组里每个项就是一个int32_t类型的整数值(范围在 -2147483648 ~ 2147483647)。还有encoding属性的值为intset_enc_int64类型的,数组里每个项取取值范围更大。

  需要注意的是:假设contents数组保存的值为2147483647, 1,2,3 四个整数值。 但只有第一个整数值需要用int32_t类型来保存,而其它三个值可以用int16_t类型来保存。不过根据整数集合的升级规则,当一个底层的int16_t数组的整数集合添加一个int64_t类型的整数值时,整数集合中所有元素都会被转换成int64_t类型。 所以contents数组保存的整数值都是int64_t类型的。

三. 升级

  当我们要将一个新元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级,然后才能将新元素添加到整数集合中。假设:集合中包含三个int16_t类型的元素,值分别是1,2,3 。因为每个元素都占用16位空间,所以整数集合底层数组的大小 为3 * 16 =48位。现将int32_t的数值65535添加进去,这里程序需要对整数集合进行升级。

  升级整数集合并添加新元素共分三步进行:

    (1) 根据新元素的类型,扩展整数集合底层数组的空间大小 ,并为新元素分配空间。分配空间后,现在整数集合4个元素的底层数组大小为4 *32 =128位, 此时前三位还是48位空间,如下图所示:

    (2) 将底层数组现有的所有元素都转换成与新元素相同的类型(需要从int16_t 转成int32_t所需的空间) ,转换后元素位置有序不变,如下图所示:

    (3) 将新元素添加到底层数组里面,如下图所示:

四. 升级的好处

  4.1 提升灵活性

    为了避免类型错误,通常不会将两种不同类型的值放在同一个数据结构里面,通过升级处理可以随意地将int16_t, int32_t, , int64_t 类型的整数添加到集合中,而不必担心出现类型错误。

4.2 节约内存

    要让一个数组可以同时保存int16_t,int32_t, , int64_t三种类型的值,最简单的做法就是直接使用int64_t类型的数组作为整数集合的底层实现,不过这样浪费内存空间。

五.   降级

  整数集合不支持降级操作,一旦对数组进行了升级,编码就会一直保持升级后的状态。即使集合里只有一个需要使用int64_t类型的元素被删除了,整数集合的编码仍然会维持intset_enc_int64, 底层数组也仍然会是int64_t类型,如下图所示:

六. 整数集合API

函数

作用

intsetNew

创建一个新的压缩列表

intsetAdd

将给定元素添加到整数集合里面

intsetRemove

从整数集合中移除给定元素

intsetFind

检查给定值是否存在于集合

intsetRandom

从整数集合中随机返回一个元素

intsetGet

取出底层数组在给定索引上的元素

intsetLen

返回整数集合包含的元素个数

intsetBloblen

返回整数集合占用的内存字节数

  

redis 系列8 数据结构之整数集合的更多相关文章

  1. Redis 的底层数据结构(整数集合)

    当一个集合中只包含整数,并且元素的个数不是很多的话,redis 会用整数集合作为底层存储,它的一个优点就是可以节省很多内存,虽然字典结构的效率很高,但是它的实现结构相对复杂并且会分配较多的内存空间. ...

  2. Redis数据结构之整数集合

    整数集合是Redis用于保存整数值的集合抽象数据结构,它可以保存类型为int16_t .int32_t或者int64_t的整数值,并且保证集合中不会出现重复元素. 一.整数集合数据结构定义 参数说明: ...

  3. Redis数据结构之整数集合-intset

    当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis会使用整数集合(intset)来存储集合元素. intset是紧凑的数组结构,同时支持16位.32位和64位整数. 结构 struc ...

  4. Redis 底层数据结构之整数集合

    文章参考:<Redis 设计与实现>黄建宏 整数集合 整数集合时集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合数量不多时,就会使用整数集合 typedef struct i ...

  5. 跟着大彬读源码 - Redis 10 - 对象编码之整数集合

    [TOC] 整数集合是 Redis 集合键的底层实现之一.当一个集合只包含整数值元素,并且元素数量不多时,Redis 就会使用整数集合作为集合键的底层实现. 1 整数集合的实现 整数集合是 Redis ...

  6. redis 系列7 数据结构之跳跃表

    一.概述 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树 ...

  7. redis源码学习_整数集合

    redis里面的整数集合保存的都是整数,有int_16.int_32和int_64这3种类型,和C++中的set容器差不多. 同时具备如下特点: 1.set里面的数不重复,均为唯一. 2.set里面的 ...

  8. Redis系列二 - 数据结构

    前言 redis作为我们开发的一大神器,我们接触肯定不会少,但是很多同学也许只会存储String类型的值,这是非常不合理的.在这里,将带大家认识Redis的5中数据结构. 1.问:Redis有那些数据 ...

  9. redis 系列5 数据结构之字典(上)

    一. 概述 字典又称符号表(symbol table),关联数组(associative array), 映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中, ...

随机推荐

  1. C++结构体与Delphi结构体相互传参,结构体中包含结构体的嵌套,数组指针

    //结构体的声明 typedef struct Mwinddirectbaseline { char* p; int s; int i; }Mwinddirectbaseline; typedef s ...

  2. 数组遍历for forEach for..in for..of

    最开始接触的遍历for 通过下标获取数组每一项的值 ,,]; ;i<num.length;i++) { console.log(num[i]) } /*打印2 5 7*/ forEach遍历数组 ...

  3. mysql数据表增删改查

    http://www.runoob.com/mysql/mysql-tutorial.html 一.MySQL 创建数据表 创建MySQL数据表需要以下信息: 表名 表字段名 定义每个表字段 语法 以 ...

  4. 加固后,上传play store, 在 google play store 下载应用安装后,打开签名校验失败

    在Google Play Console. (Google Play App Signing )签署您的应用 在创建应用时: 会有个“ Google Play App Signing” 的东西,提示使 ...

  5. PS2键盘扫描码:通码与断码

    键盘扫描码(实用于标准PC的101.102和104 键的键盘),按下发送通码,弹起发送断码. 说明: 第一类按键,通码为1字节,断码为 0xF0+通码 形式.如A键,其通码为 0x1C,断码为 0xF ...

  6. JavaScript递归

    什么是递归? 在函数的内部调用自己 下面有一个例子,通过这个例子,大家就可以了解什么是递归 function fun(){ console.log(new Date()) //获取当前时间,并在控制台 ...

  7. Jmeter中连接Oracle报错Cannot create PoolableConnectionFactory

    填坑贴,之前一直用jmeter2.13版本进行oracle测试,今天改为3.2版本,发现按照以往的方法执行测试,JDBC Request结果始终报错:Cannot create PoolableCon ...

  8. python elasticsearch 批量写入数据

    from elasticsearch import Elasticsearch from elasticsearch import helpers import pymysql import time ...

  9. 编译的时候出现 usr/bin/ld: cannot find -lxxx 的错误

    比如错误为: usr/bin/ld: cannot find -labc 首先要明白的是 labc 代表的是 libabc.so 明白了这个之后,我们就知道了问题是:在编译的过程中,需要 libabc ...

  10. maven_SSM集成的demo

    一.集成spring 二.集成springMVC 三.集成mybatis 1. pom.xml <?xml version="1.0" encoding="UTF- ...