Redis数据结构之——intset

news2024/10/6 0:29:14

写在前面

以下内容是基于Redis 6.2.6 版本整理总结

一、整数集合(intset)

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

在这里插入图片描述

1.1 整数集合的实现

整数集合可用保存的数据类型有:int16_t int32_t 和 int64_t 的整数值,并且保证集合中不会出现重复元素。
整数集合定义如下:

// src/intset.h
typedef struct intset {
    uint32_t encoding; // 编码方式,后面会详细解释
    uint32_t length;   // 集合中元素的个数,也就是contents数组的长度
    int8_t contents[]; // 保存元素的数组
} intset;

/* 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))

分析:
contents数组是整数集合的底层实现:整数集合中的每一个元素就是contents数组中的一个元素,每个元素在数组中按照从小到大的顺序排列,并且没有重复元素。

虽然,contents数组被声明为 int8_t 类型的数组,但实际上contents数组并不保存任何int8_t 类型的值,contents数组实际存储的类型取决于encoding的值。encoding的取值可以是:INTSET_ENC_INT16、INTSET_ENC_INT32 或 INTSET_ENC_INT64,每种编码的取值范围如下:

  1. INTSET_ENC_INT16 取值范围:[−2^15 ,2^15−1] 即:[-32768, 32767 ]
  2. INTSET_ENC_INT32 取值范围:[−2^31 ,2^31−1] 即:[-2147483648, 2147483647]
  3. INTSET_ENC_INT64 取值范围:[−2^63 ,2^63−1] 即:[-9223372036854775808, 9223372036854775807]

说明:n比特有符号整数的表示范围为:[−2^(n−1) ,2^(n−1)−1]

整数集合图示
在这里插入图片描述

1.2 整数集合的操作

每次往整数集合中插入新元素时,会检查新元素的类型,是不是比当前contents数组的类型长,如果是,整数集合需要先进行升级操作。比如:现在我们要把编码为int32_t的元素插入到整数集合里,因为65535的类型比集合中当前元素类型要长,所以要先对整数集合升级。
在这里插入图片描述
升级的好处

  1. 灵活:C语言是静态类型的语言,为了避免类型错误,我们通常是一种类型就放到一种类型的数据结构里,如:我们一般会使用int16_t类型的数组保存int16_t类型的值,用int32_t类型数组保存int32_t类型的值。而整数集合允许随意的将不同类型的整型值添加到集合中,而不用担心类型错误。

  2. 节约内存:如果想让一个数组同时保存,int16_t、int32_t和int64_t的值,最简单的方式就是使用int64_t类型的数组,但是这会造成空间的浪费。inset只会在适当的时候才会触发升级操作,其他时间都是尽可能节约内存。

1.2.1 intset添加元素

inset插入元素

/* 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;

    /* Upgrade encoding if necessary. If we need to upgrade, we know that
     * this value should be either appended (if > 0) or prepended (if < 0),
     * because it lies outside the range of existing values. */
    
    // 待插入整数的编码类型大于当前contents数组的编码类型,需要先进行升级操作
    if (valenc > intrev32ifbe(is->encoding)) {
        /* This always succeeds, so we don't need to curry *success. */
        return intsetUpgradeAndAdd(is,value);
    } else {
        /* Abort if the value is already present in the set.
         * This call will populate "pos" with the right position to insert
         * the value when it cannot be found. */
        // 插入之前先在集合里查找是否已经存在该元素,(二分查找)
        // 如果该元素已经存在,则直接返回;如果没有,则返回该元素待插入的数组下标,保存在pos中
        if (intsetSearch(is,value,&pos)) {
            if (success) *success = 0;
            return is;
        }
        // 1 3 5   insert 2  pos: 1
        // 扩展空间
        is = intsetResize(is,intrev32ifbe(is->length)+1);

        // 如果pos 小于当前数组长度,将从pos位置开始的元素,同一往后挪一格,腾出pos的位置
        if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
    }

    // 将待插入的元素,放在pos的位置
    _intsetSet(is,pos,value);
    // 更新数组的长度
    is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}
// 整数集合升级
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
    // 获取当前整数集合的编码
    uint8_t curenc = intrev32ifbe(is->encoding);
    // 计算待插入元素value的编码
    uint8_t newenc = _intsetValueEncoding(value);

    // 获取元素个数
    int length = intrev32ifbe(is->length);

    // 待插入的元素是否为负数
    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;
}

// 二分查找插入位置
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;
    }
}

// 1 3 5  length:3    insert: 2    pos: 1   
static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {
    void *src, *dst;
    uint32_t bytes = intrev32ifbe(is->length)-from;
    uint32_t encoding = intrev32ifbe(is->encoding);

    if (encoding == INTSET_ENC_INT64) {
        src = (int64_t*)is->contents+from;
        dst = (int64_t*)is->contents+to;
        bytes *= sizeof(int64_t);
    } else if (encoding == INTSET_ENC_INT32) {
        src = (int32_t*)is->contents+from;
        dst = (int32_t*)is->contents+to;
        bytes *= sizeof(int32_t);
    } else {
        src = (int16_t*)is->contents+from;
        dst = (int16_t*)is->contents+to;
        bytes *= sizeof(int16_t);
    }
    memmove(dst,src,bytes);
}

插入元素步骤图解
在这里插入图片描述

1.2.2 inset 删除元素:
/* Delete integer from intset */
intset *intsetRemove(intset *is, int64_t value, int *success) {
	// 获取待删除元素的编码
    uint8_t valenc = _intsetValueEncoding(value);
    uint32_t pos;
    if (success) *success = 0;

	// 如果找到待删除元素及其pos
    if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {
        uint32_t len = intrev32ifbe(is->length);

        /* We know we can delete */
        if (success) *success = 1;

        /* Overwrite value with tail and update length */
        // 把从待删除元素的后面所有节点,往前挪动一格
        if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
        is = intsetResize(is,len-1);
        is->length = intrev32ifbe(len-1);
    }
    return is;
}

二、总结

  1. 整数集合的底层实现是数组,这个数组以有序、无重复的方式存储元素,在需要时会根据新添加元素的类型升级数组的类型。
  2. 只支持升级操作,不支持降级操作
  3. 整数集合是有序集合的底层实现之一

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

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

相关文章

MetersPhere提取信息头token并设置为全局变量

MetersPhere提取信息头token并设置为全局变量 我们需要登录接口的token参数&#xff0c;如下 在下面这一行了 Set-Cookie: tokenIdA552326CDC8F4A19B454DADF8938980B; path/参考了其他的文章&#xff0c;再加上自己的理解&#xff0c;现在提供两种方法,先把先驱的脚本拿出来…

Dragonframe是一个全功能的动画制作工具,专为满足电影,广播电视和电影的要求设计。

将 Dragonframe 置于您下一部定格动画电影的核心位置。 动画片 让您的动画栩栩如生。用于精确移动的专业屏幕工具。步进、播放、捕捉、重复。 摄影 使用相机控制、测试镜头和高级图像查看工具来构图和点亮完美的镜头。 声音的 导入和编辑多个音轨。对多个角色进行对话曲目阅读。…

我的力扣刷题顺序(参考代码回忆录)

数组 数组过于简单&#xff0c;但你该了解这些&#xff01;数组&#xff1a;二分查找数组&#xff1a;移除元素数组&#xff1a;序数组的平方数组&#xff1a;长度最小的子数组数组&#xff1a;螺旋矩阵II数组&#xff1a;总结篇 链表 关于链表&#xff0c;你该了解这些&…

当光伏巡检走向全自动化

作者 | 曾响铃 文 | 响铃说 无数的太阳能面板组成光伏发电的海洋&#xff0c;在烈日下矗立&#xff0c;为了保证它们正常运行&#xff0c;电站必须安排人力巡查&#xff0c;一块块面板全面检查&#xff0c;周而复始。 在光伏发电高速发展的这些年&#xff0c;这一幕已经成为…

餐饮美食网页设计(HTML+CSS+JavaScript)

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

hiveSql 跨N天最大连续统计

hiveSql 跨N天最大连续统计说明需求分析实现最后说明 看到标题可能没太能理解&#xff0c;什么叫跨N天连续&#xff1f;这里解释下&#xff1a; 跨N天连续定义为&#xff1a;登录相隔天数小于N 用跨2天举例&#xff08;即隔一天也算连续登录&#xff09; 例如 &#xff1a; 20…

书桌台灯怎么选?分享儿童卧室灯品牌

书桌少不了的就是台灯&#xff0c;从小到大&#xff0c;不管是学习还是工作&#xff0c;都离不开一张书桌&#xff0c;学生上学学习&#xff0c;书桌就是必不可少的一部分&#xff0c;而选择怎么样的台灯好呢&#xff1f; 最重要的是安全的的光线品质 在照明领域&#xff0c;光…

Matlab 实现 信号的基本运算

实验名称 利用 Matlab 软件的信号处理工具箱(Signal Processing Toolbox)中的专用函数来实现信号的基本运算。 包括 - * 时移、翻转、展缩 运算 实验环境 MATLAB - R2021b 实验目的 了解仿真基本的信号。 sin() 产生正弦信号 ysin(x) x 是自变量 可看做时间向量 plot() …

Quick MTF 照相机镜头图像质量测试程序-UPDATE

Quick MTF&#xff0c;图像质量测试应用程序 Quick MTF 可让您测试数字图像的质量。它具有精确的计算引擎和直观的用户界面&#xff0c;无需特殊培训即可轻松使用。只需使用 Quick MTF 打开测试图像并选择目标区域。 目的 Quick MTF 检查成像设备拍摄的图像质量&#xff0c;让…

【MySQL | 运维篇】07、MySQL 分库分表之 MyCat 管理与监控

目录 一、MyCat原理 二、MyCat管理 三、MyCat-eye 3.1 介绍 3.2 安装 1). zookeeper安装 2). Mycat-web安装 3.3 访问 3.4 配置 1). 开启MyCat的实时统计功能(server.xml) 2). 在Mycat监控界面配置服务地址 3.5 测试 A. 性能监控 B. 物理节点 C. SQL统计 …

【录用案例】CCF-B类,3区智能传感器类SCIEI,仅2个月录用

3区智能传感器类SCI&EI 【出版社】ACM 出版社 【期刊简介】IF:2.0-3.0&#xff0c;JCR3区&#xff0c;中科院4区 【检索情况】SCI&EI 双检&#xff0c;正刊&#xff0c;CCF-B类 【征稿领域】 ①智能城市的绿色通信和传感器网络与机器智能&#xff08;已截稿&#…

【刷题】二叉树遍历思路解析

二叉树遍历 &#xff08;牛客网&#xff09; 题目要求&#xff1a; 编一个程序&#xff0c;读入用户输入的一串先序遍历字符串&#xff0c;根据此字符串建立一个二叉树&#xff08;以指针方式存储&#xff09;。 例如如下的先序遍历字符串&#xff1a; ABC##DE#G##F### 其中“…

预览ppt时中文乱码

现象&#xff1a;预览ppt时中文乱码 原因&#xff1a;该后端服务部署的主机没有指定中文字体 解决&#xff1a;用root角色在主机目录 /usr/share/fonts 上传Chinese字体文件 1、上传解压后&#xff0c;去应用上测试&#xff0c;发现页面全空白的&#xff0c; 且后台服务日志…

擎创技术流 | ckman教程(3)CKman源码分析部署集群的主要步骤

叮~您有一个新的技术分享已送达&#xff0c;请注意查收~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 哈喽&#xff0c;各位知乎友友们~ckman使用教程分享已经到第3期啦&#xff0c;不知道大家都掌握了没有呢&#xff1f;没有的话也没关系&#xff0c;点击下方链接&#xff0c;一键回…

centos7 + mysql 8 安装confluence7.19.3

一、安装包下载 1、访问下载连接&#xff0c;进行下载最新的安装包 Confluence Server 下载存档 | Atlassian mkdir -p /data/soft cd /data/soft wget https://www.atlassian.com/software/confluence/downloads/binary/atlassian-confluence-7.19.3.tar.gz 2、数据库准备 …

单元测试(jest):理解、安装、使用

一、理解单元测试的重要性 bug发现在开发阶段&#xff0c;成本很低&#xff0c; 如果发现在生产环境&#xff0c;成本很高&#xff0c; 如果是关键时刻&#xff0c;决定人生命运&#xff0c;决定企业发展。 从技术的角度讲&#xff0c;有效的提高代码的健壮性&#xff0c;有效…

GO语言集成开发工具环境JetBrains GoLand 2022

JetBrains GoLand 2022是一款专业的GO语言编程软件。JetBrains GoLand支持编码辅助功能&#xff0c;IDE会分析你的代码&#xff0c;然后在符号之间寻找连接。提供代码提示&#xff0c;快速导航&#xff0c;灵活的错误分析能力以及格式化和重构功能。JetBrains GoLand 2022 强大…

docker安装es+mac安装Kibana工具+es查询语法笔记

一、docker安装es 1、下载镜像 docker pull elasticsearch:7.9.0下载完后&#xff0c;查看镜像 docker images​​ 2、启动镜像 docker network create esnetdocker run -d --name es -p 9200:9200 -p 9300:9300 --network esnet -e "discovery.typesingle-node&…

图解CentOS7集群时钟同步chronyd

文章目录概述图步骤1、修改时区2、安装chrony3、选1台机作为时钟服务器4、其他机器同步时间概述 对于物理机集群&#xff0c;需要使用统一的时间&#xff0c;本文使用时钟同步技术来实现 图 图解 默认的 外部网络的 时钟服务器 在国外&#xff0c;速度较慢 0.centos.pool.n…

(表格固定尾列)bower安装的相关问题

遇到需求固定表格尾列&#xff0c;尾列是操作&#xff08;查看&#xff0c;删除&#xff09;这些 尝试了css&#xff0c;js&#xff0c;jquery&#xff0c;插件&#xff0c;layui&#xff0c;elementui各种都没用 freeze-table&#xff08;一个固定表格插件&#xff09; npm i…