Redis从入门到精通【高阶篇】之底层数据结构整数集(IntSet)详解

news2024/11/27 8:38:33

文章目录

  • 0.前言
  • 1.IntSet基本详解
    • 1.1 整数集的压缩算法原理
    • 1.2 整数集编码方式选择原理
      • 1.2.1 判断逻辑
      • 1.2.2 举例说明
  • 2. 源码解析
    • 2.1. intsetNew
    • 2.2. intsetAdd
    • 2.3. `intsetRemove`
    • 2.4. intsetFind
    • 2.5. intsetUpgradeAndAdd
    • 2.6 收获
  • 3.总结
  • 4.思考题
  • 5. Redis从入门到精通系列文章

在这里插入图片描述

0.前言

上个篇章回顾,我们上个章节我们学习了《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》,我们从源码层了解字典是一种以键值对(key-value)形式存储数据的数据结构。在 Redis 中,字典使用哈希表来实现。哈希表是一种以常数时间复杂度 O(1) 进行插入、删除和查找的数据结构。了解到在 Redis 中,字典被广泛应用于实现哈希表和集合等数据结构。

本章节,我们详细了解一下在Redis又一个底层数据结构整数集(IntSet),它是用于存储整型数据,是一种紧凑的、高效的数据结构,可以用来实现集合等功能。

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

1.IntSet基本详解

整数集的实现方式与普通的数组和链表不同,它采用了一种特殊的压缩算法,可以在尽可能少的内存空间中存储大量的整型数据。在Redis中,整数集通常用来存储集合中的元素,例如有序集合中的分值。

整数集的结构示意如下所示:

+--------+--------+--------+--------+--------+--------+
| header |  data  |  data  |  data  |  data  |  data  |
+--------+--------+--------+--------+--------+--------+

整数集由一个头部和多个数据块组成。头部中存储了整数集的元素个数、编码方式和数据块的起始地址等信息。数据块中存储了实际的整型数据。

IntSet的设计目标是尽可能地节省内存空间,同时保证高效的操作性能。它可以存储三种类型的整数:8位整数、16位整数和32位整数。IntSet的内部结构示意如下图所示:

| 32位无符号整数 | 16位无符号整数 | 8位无符号整数 | 8位标志位 |

IntSet由一个数组和一个标志位组成。数组中按照从小到大的顺序存储了所有整数值,标志位用于记录数组中存储的整数类型(即8位整数、16位整数还是32位整数)。如果标志位为0,则表示数组中存储的都是8位整数;如果标志位为1,则表示数组中存储的都是16位整数;如果标志位为2,则表示数组中存储的都是32位整数。

在IntSet中,每个整数值只会出现一次,重复的整数会被自动去重。在插入新的整数值时,IntSet会自动根据当前数组中存储的整数类型进行扩容,以保证能够存储新的整数值。

IntSet支持的操作包括插入、删除、查找、遍历等。具体来说,插入和删除操作的时间复杂度均为O(1);查找操作的时间复杂度为O(N),其中N为IntSet中存储的整数数量;遍历操作的时间复杂度为O(N)。

在Redis6中,IntSet进行了一些优化。具体来说,Redis6中的IntSet支持了快速的非对称迭代操作,这意味着可以在不需要完全遍历整个IntSet的情况下,快速地获取满足特定条件的整数值。此外,Redis6中的IntSet还支持了压缩操作,可以通过压缩来减少IntSet占用的内存空间。
在这里插入图片描述
在使用Redis时需要存储大量的整数值,可以考虑使用IntSet来优化存储空间和操作性能。在Redis6中,IntSet还支持了更多的优化和功能,可以更好地满足实际需求。
首先我们了解一下整数集的编码方式有三种:

  1. INTSET_ENC_INT16:表示整数集中的元素都是16位的整数。

  2. INTSET_ENC_INT32:表示整数集中的元素都是32位的整数。

  3. INTSET_ENC_INT64:表示整数集中的元素都是64位的整数。

整数集的编码方式是根据元素的大小来自动选择的。如果所有元素都可以放在16位或32位中,就选择相应的编码方式;否则,就选择64位编码方式。

整数集的压缩算法是在保证元素按照升序排列的前提下,尽量压缩每个元素的存储空间。具体来说,整数集会对连续的整型数据进行压缩,只存储它们的起始值和步长,而不是每个元素的实际值。这种算法可以在尽可能少的内存空间中存储大量的整型数据,提高了内存的利用率。
先不看源码,我们先看八股文。

1.1 整数集的压缩算法原理

整数集(IntSet)的压缩算法是一种特殊的算法,可以在尽可能少的内存空间中存储大量的整型数据。整数集的压缩算法基于以下两个原则:

  1. 整数集中的元素按照升序排列。

  2. 对于连续的整型数据,只存储它们的起始值和步长,而不是每个元素的实际值。

具体来说,整数集会根据元素的大小选择合适的编码方式(INTSET_ENC_INT16、INTSET_ENC_INT32或INTSET_ENC_INT64),然后将整数集中的元素按照升序排列。对于连续的整型数据,整数集会计算它们的起始值和步长,并将它们存储在数据块中。例如,假设整数集中有以下元素:1、2、3、4、5、10、11、12、13、14,整数集的数据块可以存储以下内容:

+--------+--------+--------+--------+--------+--------+--------+--------+
|   1    |   1    |   4    |   10   |   1    |   5    |   2    |   4    |
+--------+--------+--------+--------+--------+--------+--------+--------+

在上面的数据块中,第一个元素1表示整数集的编码方式(INTSET_ENC_INT16),第二个元素1表示整数集中有6个元素,后面的数据块中,每两个元素表示一个连续的整型数据的起始值和步长。例如,第三个元素4表示整数集中有4个连续的整数(4、5、6、7),第四个元素10表示这些整数的起始值,第五个元素1表示这些整数的步长。

整数集的压缩算法可以在尽可能少的内存空间中存储大量的整型数据,提高了内存的利用率。在实际的Redis应用中,整数集被广泛应用于集合等数据结构的实现。通过使用整数集,Redis可以在保证高效的操作性能的同时,减少内存的浪费,提高内存利用率。

1.2 整数集编码方式选择原理

虽然在Redis中,整数集(IntSet)的编码方式是根据元素的大小来自动选择的。整数集的编码方式有三种:INTSET_ENC_INT16、INTSET_ENC_INT32和INTSET_ENC_INT64,分别表示整数集中的元素都是16位、32位和64位的整数。

1.2.1 判断逻辑

当向整数集中添加元素时,Redis会根据新元素的大小和整数集中已有元素的大小,自动选择合适的编码方式。具体来说,如果新元素的大小可以放在整数集的当前编码方式中,就直接将新元素添加到整数集中;否则,Redis会根据新元素的大小和已有元素的大小,选择一个更大的编码方式,并将整数集中的所有元素转换为新的编码方式,然后再将新元素添加到整数集中。

1.2.2 举例说明

例如,假设整数集中已有32位整数,此时向整数集中添加一个16位整数。由于16位整数可以放在32位整数中,因此Redis会直接将新元素添加到整数集中,不需要进行编码方式的转换。但是,如果向上面的整数集中添加一个64位整数,由于64位整数无法放在32位整数中,Redis会选择64位编码方式,并将整数集中的所有元素转换为64位编码方式,然后再将新元素添加到整数集中。

整数集的自动编码方式选择可以在保证高效性能的同时,减少内存的浪费,提高内存利用率。在实际的Redis应用中,整数集被广泛应用于集合等数据结构的实现。通过使用整数集,Redis可以在保证高效的操作性能的同时,减少内存的浪费,提高内存利用率。

2. 源码解析

说了在多理论的概念和举例都略显单薄,很多同学可能在想"talk is cheap, show me code"。那么接下来我们进行一下源码解读。
在Redis的GitHub仓库中,IntSet的代码文件位于以下路径:
intset.h:https://github.com/redis/redis/blob/6.0/src/intset.h
intset.c:https://github.com/redis/redis/blob/6.0/src/intset.c
6.0分支是Redis6的分支,包含了最新的代码修改和功能更新。所以如果有同学想详细的了解一下,也可以在该分支下找到最新的IntSet代码文件,如果对Redis7的源码也想了解,只需要在上面切换一下分支即可。参考代码实现来深入了解IntSet的数据结构和操作还是很简单的。其实相比较前几个章节学习的底层数据结构,intset 不算复杂的。
在这里插入图片描述
注:如果你对C也算是了解的话,可能下面的内容就不必再看了,点击我上面的源码地址,扫一遍这两个文件基本上就一目了然。
如果你是C小白,请继续
上面截图中

intset.h:IntSet的头文件,定义了IntSet的结构体类型和相关函数的声明。
intset.c:IntSet源文件,实现了IntSet的各种操作函数,包括创建、添加、删除、查找和升级等操作函数。

从上面的源码截图我们可以看出来IntSet的定义如下:

typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;

IntSet是一个结构体类型,其中包含三个成员变量:

  • encoding:表示IntSet所使用的编码格式,可以是8位整数、16位整数或32位整数。
  • length:表示IntSet中存储的整数数量。
  • contents:表示IntSet中存储的整数值的数组,是一个灵活数组成员(flexible array member)。

接下来,我们来看一下IntSet的基本操作函数。

2.1. intsetNew

intsetNew函数用于创建一个新的IntSet结构体,并返回该结构体的指针。函数定义如下:

intset *intsetNew(void) {
    intset *is = zmalloc(sizeof(intset));
    is->encoding = INTSET_ENC_INT16;
    is->length = 0;
    return is;
}

在该函数中,使用zmalloc函数动态分配了一段大小为sizeof(intset)的内存空间,并将其初始化为0。然后将该内存空间强制转换为intset类型,并将encoding设置为INTSET_ENC_INT16,表示使用16位整数编码方式。最后返回该IntSet结构体的指针。

2.2. intsetAdd

intsetAdd函数用于向IntSet中添加一个整数值,函数定义如下:

intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    uint8_t valenc = _intsetValueEncoding(value);
    if (valenc > is->encoding) {
        return intsetUpgradeAndAdd(is,value,success);
    }
    ......
}

intsetAdd中首先调用_intsetValueEncoding函数判断value的编码方式,并将其赋值给valenc。然后判断valenc是否大于当前IntSet的编码方式,如果是,则调用intsetUpgradeAndAdd函数进行升级操作。否则,继续执行后面的操作。

接下来,根据valenc的值,将value插入到IntSet中。具体来说,如果valenc等于8,则将value转换为8位整数,并插入到IntSet的数组中;如果valenc等于16,则将value转换为16位整数,并插入到IntSet的数组中;如果valenc等于32,则将value转换为32位整数,并插入到IntSet的数组中。插入操作完成后,将success设置为1,表示插入成功。

2.3. intsetRemove

intsetRemove函数用于从IntSet中删除一个整数值,函数定义如下:

int intsetRemove(intset *is, int64_t value, int *success) {
    uint8_t valenc = _intsetValueEncoding(value);
    uint8_t *p;
    uint64_t u64;
    int32_t i32;
    int16_t i16;
    uint8_t i8;
    if (valenc <= is->encoding) {
        ......
    }
    *success = 0;
    return 0;
}

在该函数中,首先调用_intsetValueEncoding函数判断value的编码方式,并将其赋值给valenc。然后判断valenc是否小于等于当前IntSet的编码方式,如果是,则继续执行后面的操作。否则,直接返回0,表示删除失败。

接下来,根据valenc的值,在IntSet的数组中查找value,并将其删除。具体来说,如果valenc等于8,则将value转换为8位整数,并在数组中查找并删除;如果valenc等于16,则将value转换为16位整数,并在数组中查找并删除;如果valenc等于32,则将value转换为32位整数,并在数组中查找并删除。删除操作完成后,将success设置为1,表示删除成功。

2.4. intsetFind

intsetFind函数用于在IntSet中查找一个整数值,函数定义如下:

uint8_t intsetFind(intset *is, int64_t value) {
    uint8_t valenc = _intsetValueEncoding(value);
    if (valenc <= is->encoding) {
        ......
    }
    return 0;
}

在该函数中,首先调用_intsetValueEncoding函数判断value的编码方式,并将其赋值给valenc。然后判断valenc是否小于等于当前IntSet的编码方式,如果是,则继续执行后面的操作。否则,直接返回0,表示查找失败。

接下来,根据valenc的值,在IntSet的数组中查找value。具体来说,如果valenc等于8,则将value转换为8位整数,并在数组中查找;如果valenc等于16,则将value转换为16位整数,并在数组中查找;如果valenc等于32,则将value转换为32位整数,并在数组中查找。如果在数组中找到了value,则返回1,表示查找成功;否则返回0,表示查找失败。

2.5. intsetUpgradeAndAdd

intsetUpgradeAndAdd函数用于将IntSet的编码方式升级,并向IntSet中添加一个整数值,函数定义如下:

/* Upgrades the intset to a larger encoding and inserts the given integer. */
static intset *intsetUpgradeAndAdd(intset *is, int64_t value, uint8_t *success) {
    uint8_t curenc = is->encoding;
    uint8_t newenc = _intsetValueEncoding(value);
    int length = is->length;
    int prepend = value < 0 ? 1 : 0;
    is->encoding = newenc;
    while(length--) {
        int64_t v;
        _intsetGet(is,length,&v);
        intsetRemove(is,v,NULL);
        intsetAdd(is,v,success);
    }
    *success = intsetAdd(is,value,success);
    return is;
}

在该函数中,首先获取当前IntSet的编码方式curenc、新的整数值value的编码方式newenc和IntSet的长度length。然后根据value的正负性,判断是否需要在数组的开头添加新的整数值。
接下来,将IntSet的编码方式设置为newenc,并遍历IntSet中的每个整数值。对于每个整数值,先在IntSet中删除,然后重新添加到IntSet中,这样可以将所有整数值的编码方式都升级到newenc。
最后,向IntSet中添加新的整数值value,并将success设置为1,表示添加成功。

2.6 收获

通过扫一遍源码我们基本上可以了解到IntSet提供了多种操作函数,包括添加整数、删除整数、查找整数、升级整数集合的操作等。了解到这些操作函数的实现原理,包括如何进行二分查找、如何调整整数集合的大小等。它可以在O(log N)的时间复杂度内完成查找、添加、删除操作等。如何利用连续内存和二分查找来提高性能等。

3.总结

在实际的Redis应用中,整数集被广泛应用于集合等数据结构的实现。通过使用整数集,Redis可以在保证高效的操作性能的同时,减少内存的浪费,提高内存利用率。可以用于存储大量的整数值,并支持快速的插入、删除和查找操作。如果您需要存储大量的整数值,可以考虑使用IntSet来优化存储空间和操作性能。

4.思考题

应一位网友的建议,在每个章节后面留个思考题,供大家继续学习。下次分享来揭晓答案。本次的思考题只有一道。
1. Redis6是如何使用IntSet来实现集合和有序集合?

5. Redis从入门到精通系列文章

《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
在这里插入图片描述
大家好,我是冰点,今天的Redis从入门到精通【高阶篇】之底层数据结构整数集(IntSet)详解,全部内容就是这些。如果你有疑问或见解可以在评论区留言。

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

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

相关文章

【MySQL 数据库的命令操作】

目录 一、数据库的基本概念二、数据库的发展三、主流的数据库介绍五、关系数据库1、数据库的解释2、数据库的管理3、常用的数据类型4、常见的数据库结构5、SQL语句 四、MySQL 安装方法1、安装Mysql环境依赖包2、创建运行用户3、编译安装4.创建mysql用户5.修改mysql 配置文件6、…

手动将第三方资源加IOC容器中

说明&#xff1a;在SpringBoot中&#xff0c;我们可以通过在各层类上加注解&#xff08;Mapper、Service等&#xff09;声明Bean对象&#xff0c;在需要使用时&#xff0c;可直接使用AutoWirted注解自动装配。但如果是使用第三方依赖中的对象&#xff0c;因为源码不能修改&…

04 类图

类图 定义 类图显示了类(及其接口)、类的内部结构以及与其他类的联系&#xff0c;是面向对象分析和设计所得到的最重要的模型。 作用&#xff1a;可视化地表达系统的静态结构模型 类之间的几种关系&#xff1a;泛化&#xff08;Generalization&#xff09;、实现&#xff08;…

多元回归预测 | Matlab海洋捕食者算法(MPA)优化核极限学习机回归预测,MPA-KELM回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab海洋捕食者算法(MPA)优化核极限学习机回归预测,MPA-KELM回归预测,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %% …

Jenkins部署及使用

Jenkins 1.定义 1.Jenkins是一款开源CI/CD软件&#xff0c;用于自动化各种任务&#xff0c;包括构建、测试和部署软件 1.CI/CD 1.CI&#xff1a;持续集成(Continuous Integration) 1.协同开发是目前主流的开发方式&#xff0c;一般由多位开发人员同时处理同一个应用的不同模块…

vue07---elementui使用/

elementui使用 cnpm isntall -S element-ui2.9 <template><div><h1>按钮的使用</h1><el-button-group><el-button type"primary" icon"el-icon-edit"></el-button><el-button type"primary" icon&…

《实战AI低代码》生成式AI和低代码开发的融合对组织效率的影响

目录 1. 自动化重复任务: 2. 智能流程优化: 3. 增强公民开发者: 4. 快速原型设计和实验: 5. 智能应用程序维护和更新: 随着科技的不断发展,生成式人工智能(AI)和低代码软件的融合已经成为了一个热门话题。这两种技术的结合可以加速创新并改变组织运作的方式。在本…

性能测试-平均事务响应时间ART分析解析,要卷就卷成最强的...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 业务背景&#xf…

5.基于图神经网络的点云分类

目录 一、数据处理二、点云生成三、PointNet阶段1&#xff1a;通过动态图生成进行分组阶段2&#xff1a;邻居聚合 四、网络架构五、训练程序 在本教程中&#xff0c;您将学习使用图神经网络进行点云分类的基本工具。在这里&#xff0c;我们得到了一个对象或点集的数据集&#x…

北京大学研发基于机器学习的多能干细胞分化系统,高效、稳定制备功能性细胞

内容一览&#xff1a;20 世纪以来&#xff0c;干细胞与再生医学技术一直是国际生物医学领域的热点前沿之一。现如今&#xff0c;研究人员已开始探索将干细胞转变为特定类型细胞。然而&#xff0c;这一过程中干细胞会出现不规则生长或自发分化为不同类型细胞的情况&#xff0c;因…

大数据治理:数据安全

数据安全 (Data Security)一般指保护重要的、机密的纸质信息或数字信息&#xff0c;防止未经授权的非法访问、泄露、篡改、丢失、损坏、数据滥用等情形。数据安全涵盖的范围非常广泛&#xff0c;包括存储数据的硬件设备、访问数据的软件环境、访问权限控制、相关的规章制度等。…

vscode配置clangd和clang-format

vscode安装和配置 如何安装和配置vscode以搭建c开发环境&#xff0c;可以查看我的另一篇博客&#xff1a;Windows上最轻量的vscode-C开发环境搭建。 在这篇博客中&#xff0c;详细介绍了如何安装vscode以及应该安装哪些插件。这里不再赘述。 vscode中想使用clangd来作为语言…

Unity极坐标Shader特效,以及使用Instanced Property实现相同材质不同参数

Unity极坐标特效 先看看效果 Unity极坐标Shader特效 有时候我们需要在场景中摆放一些热点&#xff0c;用户点击之后出现互动&#xff0c;当然实现这个功能的方法有很多&#xff0c;作为一名程序员&#xff0c;当然是要用最简单的实现。用shader程序化实现它。 啥是极坐标 极坐…

鲸落送书第二期清华出版社系列丛书

1.《Node.js从基础到项目实践&#xff08;视频教学版)》 《Node.js从基础到项目实践&#xff08;视频教学版&#xff09;》以理论结合实践的形式&#xff0c;讲解了Node.js 基础、框架、进阶知识和项目实践。本书为视频教学版&#xff0c;每一章节都有相对应的视频讲解&#xf…

番茄工作法图解——简单易行的时间管理方法

ISBN: 978-7-115-24669-1 作者&#xff1a;【瑞典】诺特伯格&#xff08;Staffan Noteberg&#xff09; 页数&#xff1a;136页 阅读时间&#xff1a;2023-06-10 推荐指数&#xff1a;★★★★★ 番茄工作法&#xff08;意大利语&#xff1a;Pomodoro Technique&#xff09;是一…

网工内推 | 数通专场!最高19k*13薪,HCIE/CCIE认证优先

01 嘉环科技股份有限公司 招聘岗位&#xff1a;数据工程师 职责描述&#xff1a; 1、 承担TL/TE职责&#xff0c;负责数通接入&#xff08;路由器、交换机、安全、PTN、OLT等&#xff09;相关产品的工程项目交付。 2、 作为技术负责人/交付工程师支撑项目交付&#xff0c;指导…

zabbix监控域名证书期限

前言 zabbix通过自定义key"domain.discovery"发现域名&#xff08;Json格式&#xff09;&#xff0c;然后自动生成监控项&#xff0c;监控项通过自定义key"https"获取域名证书有效期&#xff0c;若少于30天则出发告警。 说明 名称作用domain.txt域名列表…

day06--java高级编程:多线程,枚举类,注解,反射,网络通讯

1 Day16–多线程01 1.1 程序概念 程序(program)&#xff1a;是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码&#xff0c;静态对象。 1.2 进程 1.2.1 概念 进程(process)&#xff1a;是程序的一次执行过程&#xff0c;或是正在运行的一个程序。是一…

基于Python3.7的robotframework环境搭建步骤

Windows环境搭建 安装Python3 官网下载&#xff0c;我这边环境是Python 3.7.0 安装robotframework基础依赖 在dos命令输入 pip install robotframework 在线安装robotframework 在dos命令输入 pip install Pypubsub3.3.0 在线安装 Pypubsub 在dos命令输入 pip install wxPy…

汇编学习教程:寻址大总结

前言 在上篇博文中&#xff0c;我们主要学习了一个全新的寄存器&#xff1a;bp。bp 寄存器在功能和使用上与 bx 有着异曲同工之妙&#xff0c;只不过两人绑定的服务对象不同&#xff1a;bx 默认绑定的是 DS 段寄存器&#xff0c;而 bp 默认绑定的是 SS 段寄存器。bx 和 bp 有着…