Redis整数集合

前言

        整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

一. 整数集合的实现

        1.1 结构

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

        每个intset.h/intset结构表示一个整数集合:

typedef struct intset {
    //编码方式
    uint32_t encoding;
    //集合中包含元素的个数
    uint32_t length;
    //保存元素的数组
    int8_t contents[];
} intset;

        content数组是整数集合的底层实现:整数集合的每一个元素都是contents数组的一个数组项。各个项在数组中按值的大小从小到大有序地排列,并且数组中不包含任何重复项。

        length属性记录了整数集合包含的元素数量,也即是contents数组长度。

        虽然intset结构contents属性声明为int8_t类型的数组。但是实际上contents数组并不保持任何int8_t类型的值。contents数组的真正类型取决于encoding属性值。

  • 如果encoding属性值为INTSET_ENC_INT16,那么contents就是一个int16_t类型的数组。数组里的每一项都是int16_t类型的整数值。
  • 如果encoding属性值为INTSET_ENC_INT32,那么contents就是一个int32_t类型的数组。数组里的每一项都是int32_t类型的整数值。
  • 如果encoding属性值为INTSET_ENC_INT64,那么contents就是一个int64_t类型的数组。数组里的每一项都是int64_t类型的整数值。

        下图展示了一个encoding为INTSET_ENC_INT64,length等于4的整数集合。contents数组按从小到大的顺序保存集合中的四个元素。因为每一个元素是int64_t类型的整数值,所以contents数组大小为sizeof(int64_t) * 4 = 64 * 4 = 256位。

        虽然contents数组保存的四个整数值中,只有-2675256175807981027真正需要int64_t类型来进行保存,而其他的1,3,4可以使用int16_t类型保存。不过根据整数集合的升级规则,当向一个底层位int16_t数组的整数集合添加一个int64_t类型的整数值时,整数集合中的所有元素都会被转化成int64_t类型。

        1.2 升级

        每当我们要将一个新元素添加到整数集合中,并且新元素的类型比整数集合现有的所有元素类型都要长时,整数集合需要先进行升级(upgrade),然后才能将新元素添加到整数集合中。

        升级步骤:

  1. 根据新元素类型,扩展整数集合底层数组的空间大小,并为新元素分配空间。
  2. 将底层数组现有元素都转化成与新元素相同的类型,并将类型转化后的元素放置到正确位置上。而在放置元素过程中,需要维持底层数组有序性不变。
  3. 将新元素添加到底层数组里。

        举个例子:

        现在有一个INTSET_ENC_INT16编码的整数集合,集合中包含三个int16_t类型的元素。纵隔占16*3=48位。

        现在要将类型int32_t的整数值65535添加到整数集合中。因为65535的类型int32_t比整数集合中的所有元素都长,所以需要对整数集合进行升级。

        升级首先要做的是,根据新类型长度,以及集合中元素数量(包括新元素),对底层数组空间进行重新分配。

        现在整数集合中又四个元素,类型需要升级位int32_t,需要空间32 * 4 = 128位,如下图。虽然程序对底层数组进行了空间重新分配,但是,数组中原有的三个元素1,2,3类型仍然是int16_t。这些元素还是保存在数组contents的前48位。

        所以程序接下来需要做的是,将原来的三个元素转化成int32_t类型。并且将转化后的元素放置到正确的位上,并且需要维持底层数组的有序性。

        首先,因为元素3在1,2,3,65535四个元素中排第三。所以它被移动到contents数组的索引2的位置上,也就是数组64位至95位的空间内。

         接着,因为元素2在1,2,3,65535四个元素中排第二。所以它被移动到contents数组的索引1的位置上,也就是数组32位至63位的空间内。

        之后, 因为元素1在1,2,3,65535四个元素中排第一。所以它被移动到contents数组的索引0的位置上,也就是数组0位至31位的空间内。

        然后, 因为元素65535在1,2,3,65535四个元素中排第四。所以它被移动到contents数组的索引3的位置上,也就是数组96位至127位的空间内。

        最后,程序将encoding属性值从INTSET_ENC_INT16修改为INTSET_ENC_INT32,并将length属性值从3修改为4。

         因为每次向整数集合中添加新元素可能会引起升级,每次升级都需要对底层数组中已有元素进行类型转换,所以向整数集合添加新元素的时间复杂度为O(N)。

升级后新元素的摆放位置:

        因为引发升级的新元素的长度总是比整数集合现有所有元素的长度都大。所以这个新元素要么大于所有现有元素,要么小于所有现有元素。

        在新元素小于所有现有元素的情况下,新元素会被放置在底层数组的最开头(索引为0)

        在新元素大于所有现有元素的情况下,新元素会被放置在底层数组的最末尾(索引为length-1)

        1.3 升级的好处

        整数集合的升级策略有两个好处,一个是提升整数集合的灵活性,二是尽可能地节约内存。

  • 提升灵活性

        因为C语言是静态类型语言,为了避免类型错误,我们一般不会将两种不同类型地值放在同一个数据结构里面。例如:我们只会用int16_t类型地数组来保存int16_t类型的值,只使用int32_t类型的数组来保存int32_t类型的值。

        因为整数集合可以通过自动升级底层数组来适应新元素,我们可以随意将int16_t,int_32_t或int64_t类型的整数添加到集合中,而不用担心类型错误。

  • 节约内存

        让一个数组既能保存int16_t,int32_t或者int64_t类型的值最简单的做法就是直接使用int64_t类型的数组作为整数集合底层数组的实现。但是,这样一来,即使添加到整数集合里面的都是int16_t类型的值或者int32_t类型的值,数组都会需要用int64_t类型来保存,从而出现浪费内存的问题。

        而整数集合的做法既可以让集合同时保存三个类型的值,又可以确保升级操作只会在有需要的时候进行,这样实现尽可能地节约内存。

            1.4 降级

        整数集合不支持降级操作,一旦数组进行升级,编码就会一直保存升级后地状态。

        举个例子,即使我们将集合中唯一一个真正需要int64_t类型来保存地元素删除了,整数集合地编码(encoding)仍然会保持INTSET_ENC_INT64,底层数组地类型仍然是int64_t类型。

         1.5 整数集合API

 int intsetValidateIntegrity(const unsigned char *is, size_t size, int deep): 验证数据结构的完整性。当“deep”为0时,仅验证标头的完整性。当“deep”为1时,我们会确保没有重复或无序的记录。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/178324.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】进程间通信——进程间通信的介绍和分类、管道、匿名管道、命名管道、匿名管道与命名管道的区别

文章目录 进程间通信1.进程间通信的介绍1.1目的和发展 2.进程间通信分类3.管道3.1匿名管道3.1.1匿名管道的原理(文件角度)3.1.2匿名管道的原理(内核角度)3.1.3管道读写规则3.1.4管道特点 3.2命名管道3.2.1创建命名管道3.2.2命名管…

【计算机网络学习之路】TCP socket编程

文章目录 前言一. 服务器1. 初始化服务器2. 启动服务器 二. 客户端三. 多进程服务器结束语 前言 本系列文章是计算机网络学习的笔记,欢迎大佬们阅读,纠错,分享相关知识。希望可以与你共同进步。 本篇博客基于UDP socket基础,介绍…

【SpringCloud】认识微服务、服务拆分以及远程调用

SpringCloud 1.认识微服务 1.1单体架构 单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署 单体架构的优缺点: 优点: 架构简单,部署成本低 缺点: 耦合度高(维护困难&#x…

设备状态监测与故障诊断系统的作用

随着工业生产的发展和技术的进步,设备状态监测与故障诊断系统在工业领域中扮演着越来越重要的角色。这一系统通过实时监测设备的状态和参数,及时发现潜在的故障,并提供预警信号,以降低生产中断、提高安全性和维护效率。以下将详细…

Django 模型和Admin站点管理(三)

一、定义模型 (1) 创建模型类,必须要继承自 models.Model from django.db import models# Create your models here. #设计数据库 #创建模型 class UserModel(models.Model):namemodels.CharField(max_length30) #对应于SQL name varchar(30…

慕尼黑电子展Samtec Demo | 回环测试带来Samtec产品组合优异表现

【摘要/前言】 大家好!Electronica虎家展台Demo系列回来咯。 实践出真知,再好的纸面数据都不如来一场实际的测试和演示。Samtec团队始终在努力为客户带来卓越的产品和优质服务。而这其中,Demo演示的存在至关重要。演示过程可以为大家带来了…

ubuntu编译sqlite3并使用

SQLite3是一种轻量级的关系型数据库管理系统,它是在C语言基础上实现的。SQLite3具有许多优点,例如: 1.灵活:它可以在多种操作系统上运行,并且可以将多个数据库文件合并成一个文件。 2.易于使用:SQLite3使用…

“云浮云福保”暖心回归! 保障升级价格不变,医保个账可为全家缴费!

11月22日,2024年“云浮云福保”项目启动会在广东省云浮市迎宾馆成功举办。记者在会上获悉,“云浮云福保”是在云浮市医疗保障局、云浮市金融工作局、国家金融监督管理总局云浮监管分局指导下,的指导下,由中国人民财产保险股份有限…

网络安全之渗透测试入门准备

渗透测试入门所需知识 操作系统基础:Windows,Linux 网络基础:基础协议与简单原理 编程语言:PHP,python web安全基础 渗透测试入门 渗透测试学习: 1.工具环境准备:①VMware安装及使用&#xff1b…

Modbus转Profinet网关连接PLC与天信流量计通讯案例

本文将为您详细介绍如何成功连接PLC与天信流量计:从选择合适的Modbus转Profinet网关开始,到设置网关以实现通讯连接,还会涵盖部署和故障排除过程中可能遇到的一些问题。 首先,选择合适的Modbus转Profinet网关至关重要。我们选用基…

竞赛python区块链实现 - proof of work工作量证明共识算法

文章目录 0 前言1 区块链基础1.1 比特币内部结构1.2 实现的区块链数据结构1.3 注意点1.4 区块链的核心-工作量证明算法1.4.1 拜占庭将军问题1.4.2 解决办法1.4.3 代码实现 2 快速实现一个区块链2.1 什么是区块链2.2 一个完整的快包含什么2.3 什么是挖矿2.4 工作量证明算法&…

微信小游戏上线流程

微信小游戏上线是一个需要经过一系列步骤的过程。以下是一个一般性的微信小游戏上线流程,请注意,上述步骤可能会有微信平台的政策和规定的变化,因此建议在开发过程中及时查阅微信小游戏的官方文档和最新政策。北京木奇移动技术有限公司&#…

前后端分离SpringBoot+vue的买菜农副产品多功能商城

1,项目介绍 本系统主要针对买菜而设计,其功能有菜品基本信息管理、商品类别管理、系统订单管理、评论管理、系统用户管理等功能模块。并且本系统采用了现在流行的SpringBootVue进行的设计与实现,其中Tomcat为服务器,MySQL为数据库…

SpringBoot定时任务(一看就会)

一、引入依赖 定时任务是spring boot框架提供的基础能力之一,所以其依赖是在spring-boot-starter里面,但是一般开发的时候我们直接引入web依赖即可,web依赖中包含了spring-boot-starter。要注意的是Spring Boot 从版本1.3.0开始提供对定时任务…

写单元测试,没你想得那么简单!

前言 单元测试是什么我们就简单介绍一下: 单元测试是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。 接下来是本人对单元测试的理解和实践。里面没有废话,希望每句话能说到你心…

LangChain: 类似 Flask/FastAPI 之于 Django,LangServe 就是「LangChain 自己的 FastAPI」

原文:LangChain: 类似 Flask/FastAPI 之于 Django,LangServe 就是「LangChain 自己的 FastAPI」 - 知乎 说明:LangServe代替 langchainserver 成为新的langchain 部署工具 官网资料:🦜️🏓 LangServe | &…

批量插入SQL 错误 [933] [42000]: ORA-00933: SQL 命令未正确结束

使用DBeaver向【oracle数据库】插入大量数据 INSERT INTO Student(name,sex,age,address,birthday) VALUES(Nike,男,18,北京,2000-01-01) ,(Nike,男,18,北京,2000-01-01) ,(Nike,女,18,北京,2000-01-01) ,(Nike,女,18,北京,2000-01-01) ,(Nike,男,18,北京,2000-01-01) ,(Nike…

【JDK源码阅读】什么是 avoid getfield opcode ?

说明:JDK源码版本为 Oracle JDK 8 1. 背景 阅读 java.lang.String 的源码,会发现有些地方注释为/* avoid getfield opcode */,此处的代码是将当前类定义的成员变量引用为本地变量,从字面意思理解,是为了避免使用 get…

计算机基础知识——字,字节,进制,short,byte等

目录 进制位,字节,字Byte,ShortByteBuf有符号数和无符号数 进制 HEX,Hexadecimal ,十六进制。 DEC,Decimal ,十进制。 OCT,Octal ,八进制。 BIN,Binary &a…

基于java和uniapp的即时聊天源码

聊天IM,支持单聊、群聊、朋友圈、摇一摇、附近的人、收藏、扫码、机器人、文字、图片、名片、实时音视频通话等功能。用uniapp开发,支持打包成多终端! 推送:uniPush websocket资源:阿里OSS(图片、声音、视…
最新文章