Redis中的整数集合(IntSet)

news2024/11/25 0:41:21

Redis节省内存的两个优秀设计思想:一个是使用连续的内存空间,避免内存碎片开销;二个是针对不同长度的数据,采用不同大小的元数据,以避免使用统一大小的元数据,造成内存空间的浪费。IntSet便具备以上两个设计思想。IntSet是Redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。

IntSet的结构

来看看Redis 6.2.4对其结构的定义

typedef struct intset {
    uint32_t encoding; /*编码方式,支持存放16位、32位、64位整数*/
    uint32_t length; /* 元素个数 */
    int8_t contents[]; /整数数组,保存集合数据 */
} intset;

其中encoding包含三种模式,表示存储的整数大小不同

/* Note that these encodings are ordered, so:
 * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t)) /*两个字节*/
#define INTSET_ENC_INT32 (sizeof(int32_t)) /*四个字节*/
#define INTSET_ENC_INT64 (sizeof(int64_t)) /*八个字节*/

为了方便查找,Redis会将其按照升序进行排列,保存在contents[]数组中。例如下图
在这里插入图片描述

1byte = 8bit,int16 也就是2个字节。所以上图所示的set集合总字节大小为 encoding 4 + length 4 + 3 * 2byte = 14byte。 当这个集合的每个元素的大小固定之后,我们寻址就非常方便了。

在这里插入图片描述

我们可以观察出一个寻址公式:address[i] = startPtr + (encoding) * index

IntSet添加元素

文字描述

在集合中利用二分查找找要插入的元素位置pos

找到—修改,return

未找到得到元素应当插入的位置

判断元素的位数是否超过了curEncoding

未超过 ,将pos之后的元素全部后移一位,插入新的元素

超过,创建新的intset,更新所有元素的编码方式,倒序依次将数组中的元素拷贝到扩容 后的正确位置,新元素比0大放队首,比0小放队尾

简单的流程图

在这里插入图片描述

源代码

看下面的Redis源代码

/* Search for the position of "value". Return 1 when the value was found and
 * sets "pos" to the position of the value within the intset. Return 0 when
 * the value is not present in the intset and sets "pos" to the position
 * where "value" can be inserted. */
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
    int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
    int64_t cur = -1;

    /* The value can never be found when the set is empty */
    if (intrev32ifbe(is->length) == 0) {
        if (pos) *pos = 0;
        return 0;
    } else {
        /* Check for the case where we know we cannot find the value,
         * but do know the insert position. */
        if (value > _intsetGet(is,max)) {
            if (pos) *pos = intrev32ifbe(is->length);
            return 0;
        } else if (value < _intsetGet(is,0)) {
            if (pos) *pos = 0;
            return 0;
        }
    }
    while(max >= min) {
        mid = ((unsigned int)min + (unsigned int)max) >> 1;
        cur = _intsetGet(is,mid);
        if (value > cur) {
            min = mid+1;
        } else if (value < cur) {
            max = mid-1;
        } else {
            break;
        }
    }
    if (value == cur) {
        if (pos) *pos = mid;
        return 1;
    } else {
        if (pos) *pos = min;
        return 0;
    }
}
/* Insert an integer in the intset */
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    uint8_t valenc = _intsetValueEncoding(value); //获取当前编码值 
    uint32_t pos; //要插入的位置 
    if (success) *success = 1;//判断编码是不是超过了intset的编码
	 
    if (valenc > intrev32ifbe(is->encoding)) { //
        //超过了编码,要升级 8 -> 16 / 16 -> 32 / 32 -> 64 
        return intsetUpgradeAndAdd(is,value);
    } else {
        //没有超过,寻找与value值一样的元素 
        if (intsetSearch(is,value,&pos)) {
        	//找到了,直接返回。set集合是无重复的 
            if (success) *success = 0;
			return is;
        }
		//为数组扩容 + 1 位 
        is = intsetResize(is,intrev32ifbe(is->length)+1);
        //将pos之后的元素全部往后挪一位 
        if (pos < intrev32ifbe(is->length)) 
			intsetMoveTail(is,pos,pos+1);
    }
	//插入新元素 
    _intsetSet(is,pos,value);
    //重置元素长度 
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}

注意:此处并不需要挪所有元素,因为编码格式一旦超过,必然是最大整数或者最小整数。

/* Upgrades the intset to a larger encoding and inserts the given integer. */
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
	//获取当前intset编码 
    uint8_t curenc = intrev32ifbe(is->encoding);
    //计算新的编码值 
    uint8_t newenc = _intsetValueEncoding(value);
    //获取元素个数 
    int length = intrev32ifbe(is->length);
    //判断元素是否比 0 大,大于插入队尾,小于插入队首 
	int prepend = value < 0 ? 1 : 0;

    /* First set new encoding and resize */
    //重新为数组编码 
    is->encoding = intrev32ifbe(newenc);
    //重置数组大小 
	is = intsetResize(is,intrev32ifbe(is->length)+1); 
    
	/* Upgrade back-to-front so we don't overwrite values.
     * Note that the "prepend" variable is used to make sure we have an empty
     * space at either the beginning or the end of the intset. */
    
	//倒序遍历,逐个搬运元素到新的位置 
    while(length--) //按照新的编码方式插入元素 
        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));

    /* Set the value at the beginning or the end. */
    if (prepend)
        _intsetSet(is,0,value);
    else
        _intsetSet(is,intrev32ifbe(is->length),value);
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}

总结

Intset可以看做是特殊的整数数组,具备一些特点:

  • Redis会确保Intset中的元素唯一、有序
  • 具备类型升级机制,可以节省内存空间
  • 底层采用二分查找方式来查询

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

160套小程序源码

源码列表如下&#xff1a; AppleMusic (知乎日报) 微信小程序 d artand 今日更新求职招聘类 医药网 口碑外卖点餐 城市天气 外卖小程序 定位天气 家居在线 微信小程序-大好商城&#xff0c;wechat-weapp 微信小程序的掘金信息流 微信跳一跳小游戏源码 微票源码-demo 急救应急处…

MyBatis- plus

实战总结 1.批量插入性能 1.批量插入性能差的原因 使用saveBatch()方法时&#xff0c; MySQL JDBC驱动在默认情况下会无视executeBatch()语句&#xff0c;把我们期望批量执行的一组sql语句拆散&#xff0c;一条一条地发给MySQL数据库&#xff0c;批量插入实际上是单条插入&a…

2023企业真实性能测试常见面试题分析

简述性能测试流程&#xff1f; 1.分析性能需求。挑选用户使用最频繁的场景来测试&#xff0c;比如&#xff1a;登陆&#xff0c;搜索&#xff0c;下单等等。确定性能指标&#xff0c;比如&#xff1a;事务通过率为100%&#xff0c;TOP99%是5秒&#xff0c;最大并发用户为1000人…

Three.js——八、坐标、更改模型原点、移除、显示隐藏模型对象

世界坐标.getWorldPosition() 基础坐标也就是模型的.position属性 世界坐标&#xff1a;就是模型资深.position和所有父对象.position累加的坐标 用.getWorldPosition()属性需要用三维向量表示摸个坐标后方可读取 例如&#xff1a; const geometry new THREE.BoxGeometry(10…

【Qt】createEditor进不去【2023.05.07】

摘要 妈卖批&#xff0c;因为这个函数进不去&#xff0c;emo了一下午。实际上就是因为函数声明和定义的地方漏了个const关键字。 1.正确✔&#xff1a; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) cons…

Rocketmq面试(三)消息积压,增加消费者有用么?

目录 一.广播模式和集群模式的不同 二.延迟拉取 三.消费者延迟拉取消息的原因 四.增加消费者后是如何分配MessageQueue(引出负载策略) 一.广播模式和集群模式的不同 首先我们要强调一下。在广播模式&#xff08;每条消息需要被消费者组中的每个消费者处理&#xff0c;也就是…

QT/PyQT/PySide 通过富文本形式实现关键词高亮

因为本质上都是QT&#xff0c;所以我标题带了QT&#xff0c;这个思路是没问题的&#xff0c;就是用C得换个语言。 最开始想根据之前一篇博客的思路进行高亮 PyQT/PySide 文本浏览器跳转到指定行&#xff0c;并高亮指定行_qt 指定行高亮_Toblerone_Wind的博客-CSDN博客https:/…

Linux 设备树文件手动编译的 Makefile

前言 通过了解 Linux 设备树的编译方法&#xff0c;手动写了一个可以把 dts、dtsi、设备树依赖头文件等编译为设备树 dtb 的 Makefile Makefile 如下 mkfile_path : $(abspath $(lastword $(MAKEFILE_LIST))) cur_makefile_path : $(dir $(mkfile_path))DIR_ROOT : $(cur_ma…

十三届蓝桥杯国赛2022

会得噶 A 2022B 钟表C 卡牌D 最大数字dfsF 费用报销&#xff08;不是根据收据个数&#xff0c;而是根据日期dp)H 机房&#xff08;最近公共祖先lca&#xff09;I 齿轮J 搬砖&#xff08;贪心01背包&#xff09; A 2022 #include <bits/stdc.h> using namespace std; int …

Openlayers如何设置米作为作为圆形的真实半径,解决圆形半径跟随地图缩放同时缩放的失真问题

专栏目录: OpenLayers入门教程汇总目录 前言 相信找到这篇文章的同学肯定遇到了Openlayers直接设置圆形半径( radius)单位不准确的问题,而且失真严重。这是因为默认圆形半径设置的是浏览器像素大小,而不是真实地理信息中的半径长度。那么怎么进行转换成我们现实中的“米…

python+vue校园快递代取系统的设计与实现3i0v9

开发语言&#xff1a;Python 框架&#xff1a;django/flask Python版本&#xff1a;python3.7.7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm 本系统名为“基于vue快递代取系统”&#xff0c;系统主要适用于毕业设计&#xff0c;不…

【数据分享】1929-2022年全球站点的逐日最高气温(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01; 之前我们分享过1929-2022年全球气象站…

高通滤波学习(opencv)

以下代码参考视频解析 这段代码使用了二维FFT变换对输入图像进行频域处理&#xff0c;并设计了一个简单的高通滤波器。 前两行使用了numpy库中的fft2函数对输入图像image进行二维傅里叶变换&#xff08;FFT&#xff09;。接着&#xff0c;fft_shift函数将转化后的频谱数据fft…

什么是DOM和BOM?

一、什么是DOM DOM 全称是 Document Object Model&#xff0c;也就是文档对象模型。提供操作页面元素的方法和属性&#xff0c;是HTML和XML的API&#xff0c;DOM把整个页面规划成由节点层级构成的文档。 DOM 树 DOM树是Web页面的模型&#xff0c;当浏览器加载一个Web页面时&am…

A Framework for Evaluating Gradient Leakage Attacks in Federated Learning

联邦学习中梯度泄漏攻击评估框架 摘要&#xff1a; 针对问题&#xff1a;从客户端向联邦服务器共享本地参数更新也可能容易受到梯度泄漏攻击&#xff0c;并侵犯客户端关于其训练数据的隐私。 提出了一个原则性框架&#xff0c;用于评估和比较不同形式的客户端隐私泄露攻击。…

路径规划算法:基于纵横交叉优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于纵横交叉优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于纵横交叉优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

Eclipse导入项目的配置步骤说明

1.数据库创建并导入 &#xff08;1&#xff09;打开navicat&#xff0c;右击&#xff0c;选择创建数据库&#xff0c;进入新建数据库页面&#xff0c;输入数据库名称。我这里创建的是report数据库。 &#xff08;2&#xff09;右击自己创建的数据库&#xff0c;选择运行sql文件…

【C++】STL——stack OJ练习

文章目录 1. 最小栈思路分析AC代码拓展思维 2. 栈的压入、弹出序列思路讲解AC代码 3. 逆波兰表达式求值思路讲解AC代码拓展&#xff1a;中缀表达式如何转后缀 这篇文章我们来做几道stack相关的OJ题&#xff0c;练习一下stack的使用。 1. 最小栈 先来看第一道题——&#xff1a…

【人工智能】常见问题以及解答

1 什么是人工智能 人工智能&#xff08;Artificial Intelligence, AI&#xff09;是一门涉及计算机科学、数学、心理学、哲学等多个领域的交叉学科&#xff0c;旨在研究如何使计算机能够像人一样地思考、学习和行动。 在过去几十年中&#xff0c;人工智能技术得到了广泛的应用…

LNMP部署

LNMP部署 一、安装Nginx服务二、安装mysql服务三、安装配置PHP解析环境四、部署 Discuz&#xff01;社区论坛 Web 应用五、fpm参数优化 LNMP架构&#xff1a; LNMP代表的就是&#xff1a;Linux系统下NginxMySQLPHP这种网站服务器架构Linux是一类Unix计算机操作系统的统称&#…