Redis数据结构:Set类型全面解析

news2024/11/23 8:25:54

Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。相对于列表,集合也有两个特点:无序、不可重复

一个集合最多可以存储 2^32-1 个元素。概念和数学中个的集合基本类似,数学集合的概念是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。

简而言之,Redis 集合就是一些不重复值的组合。利用集合(Set)这个数据结构,Redis 可以存储一些集合类型的数据,Redis也通过一些简便的命令很好的支持了交集、并集和差集等集合的基本运算。


文章目录

    • @[toc]
        • 1、Set数据类型
          • 1.1、Set类型简介
          • 1.2、Set应用场景
        • 2、Set底层结构
          • 2.1、List底层结构介绍
          • 2.2、整数集合IntSet
          • 2.3、哈希表HashTable
        • 3、Set常用命令
          • 3.1、添加集合元素
          • 3.2、查看集合所有值
          • 3.3、判断一个值是否在集合中
          • 3.4、查看某集合的存值的数量
          • 3.5、删除集合中指定值的元素
          • 3.6、随机选出某集合中一个元素
          • 3.7、随机删除某集合中一个元素
          • 3.8、将一个集合中的某值移动至另一个集合
          • 3.9、集合运算:差集
          • 3.10、集合运算:交集
          • 3.11、集合运算:并集

1、Set数据类型

1.1、Set类型简介

Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。相对于列表,集合也有两个特点:无序、不可重复

一个集合最多可以存储 2^32-1 个元素。概念和数学中个的集合基本类似,数学集合的概念是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。

简而言之,Redis 集合就是一些不重复值的组合。利用集合(Set)这个数据结构,Redis 可以存储一些集合类型的数据,Redis也通过一些简便的命令很好的支持了交集、并集和差集等集合的基本运算。

1.2、Set应用场景

常见的应用场景有:投票系统、标签系统、共同好友、共同关注、共同爱好、抽奖、商品筛选栏,访问 IP 统计等

使用场景:

  • 点赞、踩、收藏:Set 类型可以保证一个用户只能点一个赞;
  • 共同关注、标签:Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等;
  • 抽奖活动:存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次

2、Set底层结构

2.1、List底层结构介绍

Redis Set 的底层存储采用 整数集合 IntSet 和哈希表,二者是相互转换的,使用 IntSet 存储必须满足下面两个条件,否则使用 HashTable,条件如下:

  • 结合对象保存的所有元素都是整数值;
  • 集合对象保存的元素数量不超过 512 个

以 Set 的 SADD 命令为例子,整个添加过程如下:

  • 检查 Set 是否存在不存在则创建一个 Set 结合。
  • 根据传入的 Set 集合一个个进行添加,添加的时候需要进行内存压缩。
  • setTypeAdd 执行 Set 添加过程中会判断是否进行编码转换
void saddCommand(redisClient *c) {
    robj *set;
    int j, added = 0;
 
    // 取出集合对象
    set = lookupKeyWrite(c->db,c->argv[1]);
 
    // 对象不存在,创建一个新的,并将它关联到数据库
    if (set == NULL) {
        set = setTypeCreate(c->argv[2]);
        dbAdd(c->db,c->argv[1],set);
 
    // 对象存在,检查类型
    } else {
        if (set->type != REDIS_SET) {
            addReply(c,shared.wrongtypeerr);
            return;
        }
    }
 
    // 将所有输入元素添加到集合中
    for (j = 2; j < c->argc; j++) {
        c->argv[j] = tryObjectEncoding(c->argv[j]);
        // 只有元素未存在于集合时,才算一次成功添加
        if (setTypeAdd(set,c->argv[j])) added++;
    }
 
    // 如果有至少一个元素被成功添加,那么执行以下程序
    if (added) {
        // 发送键修改信号
        signalModifiedKey(c->db,c->argv[1]);
        // 发送事件通知
        notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id);
    }
 
    // 将数据库设为脏
    server.dirty += added;
 
    // 返回添加元素的数量
    addReplyLongLong(c,added);
}

稍微深入分析一下set的单个元素的添加过程,首先如果已经是 HashTable 的编码,那么我们就走正常的 HashTable 的元素添加,如果原来是 IntSet 的情况,那么我们就需要进行如下判断:

  • 如果能够转成 int 的对象(isObjectRepresentableAsLongLong),那么就用 IntSet 保存。
  • 如果用 IntSet 保存的时候,如果长度超过5 12(REDIS_SET_MAX_INTSET_ENTRIES)就转为 HashTable 编码。
  • 其他情况统一用 HashTable 进行存储。
2.2、整数集合IntSet

整数集合 IntSet 是 Redis用来保存整数值的集合的一种数据结构,可以用来保存 int 类型数据,并且可以保证不会出现重复元素。因此当一个集合中只包含整数元素且数量不多的时候,Redis 会选择使用整数集合作为底层实现。

IntSet 内部其实是一个数组(int8_t coentents[] 数组),而且存储数据的时候是有序的,因为在查找数据的时候是通过二分查找来实现的。

img

如果你的集合只有整数值元素,并且数量是轻量的,这时候 Redis 会使用使用整数集合作为 Redis 集合的底层数据结构。参考如下代码:

typedef struct IntSet{
     // 编码格式
     uint32_t encoding;
     // 集合中的元素个数
     uint32_t length;
     // 保存元素数据
     int8_t contents[];
} IntSet;

我们拆解下:

属性说明
“encoding”编码方式
“length”数组中元素个数,也就是数组的整体长度
“contents[]”整数集合,集合的每个元素都是数组的一个数组项(item)。具有特点:按值的大小增序排列、不包含任何重复项

“contents” 是整数集合的底层实现,保存了整数集合的每一个元素,每个元素在该数组中从小到大有序排列,并且不重复(如何保证有序性和唯一性我们后面讨论插入的时候在说)。“contents” 数组虽然声明为 int8_t 类型,但其实真正的类型取决于 “encoding” 的值。在操作一个整数集合的时候,会首先获取 “encoding” 的值。

举个栗子,当我们执行 SADD numbers 1 3 5 向集合对象插入数据时,该集合对象在内存的结构如下:

image-20230823235054892

2.3、哈希表HashTable

Redis 中的 key-value 是通过 dictEntry 对象来实现的,而哈希表就是将 dictEntry 进行了再一次的包装得到的,这就是哈希表对象 dictht:

typedef struct dictht {
    dictEntry **table;//哈希表数组
    unsigned long size;//哈希表大小
    unsigned long sizemask;//掩码大小,用于计算索引值,总是等于size-1
    unsigned long used;//哈希表中的已有节点数
} dictht;

PS:table 是一个数组,其每个元素都是一个 dictEntry 对象。

hashtable 编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象对应一个集合元素,字典的值都是 NULL。当我们执行 SADD fruits "apple" "banana" "cherry" 向集合对象插入数据时,该集合对象在内存的结构如下:

image-20230823235716144


3、Set常用命令

3.1、添加集合元素

使用 SADD 命令添加集合元素

SADD set value

若值已存在,则不进行添加,并返回 0

image-20230821235344528

3.2、查看集合所有值

使用 SMEMBERS 命令查看集合所有值

SMEMBERS set

image-20230821235614136

3.3、判断一个值是否在集合中

使用 SISMEMBER 命令判断一个值是否在集合中

image-20230821235954832

3.4、查看某集合的存值的数量

使用 SCARD 命令查看某集合的存值的数量

SCARD set

image-20230822000410786

3.5、删除集合中指定值的元素

使用 SREM 删除集合中指定值的元素

SREM set value

image-20230822000710429

3.6、随机选出某集合中一个元素

使用 SRANDMEMBER 命令随机选出某集合中一个元素

SRANDMEMBER set

image-20230822000949807

3.7、随机删除某集合中一个元素

使用 SPOP 命令随机删除某集合中一个元素

SPOP set

image-20230822001227634

3.8、将一个集合中的某值移动至另一个集合

使用 SMOVE 命令 将一个集合中的某值移动至另一个集合

SMOVE source target value

image-20230822001457709

3.9、集合运算:差集

使用 SDIFF 命令进行集合运算:差集

SDIFF set1 set2

image-20230822001906994

3.10、集合运算:交集

使用 SINTER 命令进行集合运算:交集

SINTER set1 set2

image-20230822002039149

3.11、集合运算:并集

使用 SUNION 命令进行集合运算:并集

SUNION set1 set2

image-20230822001939037

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

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

相关文章

【内网穿透】搭建我的世界Java版服务器,公网远程联机

目录 前言 1. 搭建我的世界服务器 1.1 服务器安装java环境 1.2 配置服务端 2. 测试局域网联机 3. 公网远程联机 3.1 安装cpolar内网穿透 3.1.1 windows系统 3.1.2 linux系统&#xff08;支持一键自动安装脚本&#xff09; 3.2 创建隧道映射内网端口 3.3 测试公网远程…

Spring AOP基于注解方式实现和细节

目录 一、Spring AOP底层技术 二、初步实现AOP编程 三、获取切点详细信息 四、 切点表达式语法 五、重用&#xff08;提取&#xff09;切点表达式 一、Spring AOP底层技术 SpringAop的核心在于动态代理&#xff0c;那么在SpringAop的底层的技术是依靠了什么技术呢&#x…

国产AI芯片突破,芯片或成白菜价,恐惧的美芯阻止台积电为它代工

日前消息指台积电大幅减少一家中国AI芯片企业的产能&#xff0c;原因在于国产AI芯片的性能已接近美芯&#xff0c;美国芯片企业NVIDIA与相关的资本机构贝莱德联手施压台积电所致&#xff0c;凸显出美国芯片忧虑中国AI芯片的竞争力。 这家国产AI芯片企业为壁仞科技&#xff0c;据…

C#,《小白学程序》第七课:列表(List)应用之一————编制高铁车次信息表

1 文本格式 /// <summary> /// 车站信息类 class /// </summary> public class Station { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; /// <summary> /// 车站名 /// </summary>…

【JavaSE专栏89】Java字符串和XML数据结构的转换,高效灵活转变数据

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;3年JAVA全栈开发经验&#xff0c;专注JAVA技术、系统定制、远程指导&#xff0c;致力于企业数字化转型&#xff0c;CSDN学院、蓝桥云课认证讲师。 主打方向&#xff1a;Vue、SpringBoot、微信小程序 本文讲解了 XML 的概…

软件测试的方法有哪些?

软件测试 根据利用的被测对象信息的不同&#xff0c;可以将软件测试方法分为&#xff1a;黑盒测试、灰盒测试、白盒测试。 1、白盒测试 1&#xff09;概念&#xff1a;是依据被测软件分析程序内部构造&#xff0c;并根据内部构造分析用例&#xff0c;来对内部控制流程进行测试…

基于Dpabi的功能连接

1.预处理 这里预处理用Gretna软件进行&#xff0c;共分为以下几步&#xff1a; &#xff08;1&#xff09;DICOM转NIfTI格式 (2)去除前10个时间点(Remove first 10 times points)&#xff1a;由于机器刚启动、被试刚躺进去也还需适应环境&#xff0c;导致刚开始扫描的数据很…

macOS 安装 Homebrew 详细过程

文章目录 macOS 安装 Homebrew 详细过程Homebrew 简介Homebrew 安装过程设置环境变量安装 Homebrew安装完成后续设置(重要)设置环境变量homebrew 镜像源设置macOS 安装 Homebrew 详细过程 本文讲解了如何使用中科大源安装 Homebrew 的安装过程,文章里面的所有步骤都是必要的,需…

ExpressLRS开源之RC链路性能测试

ExpressLRS开源之RC链路性能测试 1. 源由2. 分析3. 测试方案4. 测试设计4.1 校准测试4.2 实验室测试4.3 拉距测试4.4 遮挡测试 5. 总结6. 参考资料 1. 源由 基于ExpressLRS开源基本调试验证方法&#xff0c;对RC链路性能进行简单的性能测试。 修改设计总能够满足合理的需求&a…

Streamlit 讲解专栏(十一):数据可视化-图表绘制详解(中)

文章目录 1 前言2 绘制交互式散点图3 定制图表主题4 增强数据可视化的交互性与注释步骤1步骤二 5 结语 1 前言 在上一篇博文《 Streamlit 讲解专栏&#xff08;十&#xff09;&#xff1a;数据可视化-图表绘制详解&#xff08;上&#xff09;》中&#xff0c;我们学习了一些关…

Vue脚手架中安装ElementUi

目录 ElementUi简介&#xff1a; ElementUi下载&#xff1a; npm 安装&#xff1a; 引入ElementUi: 测试是否引入成功&#xff1a; Element-ui官网&#xff1a;组件 | Element ElementUi简介&#xff1a; ElementUi&#xff0c;是由国内的饿了么团队开发并开源的一套为开…

美五代机装备激光武器可行性分析

源自&#xff1a;北京蓝德信息科技有限公司 一、SHiELD项目研究进展分析 图表&#xff1a;SHiELD项目主要情况 二、机载激光武器面临的技术挑战分析 三、五代机装备激光武器的可行性 声明:公众号转载的文章及图片出于非商业性的教育和科研目的供大家参考和探讨&#xff0c;并不…

三维模型OBJ格式轻量化压缩处理效率提高的技术方法探讨

三维模型OBJ格式轻量化压缩处理效率提高的技术方法探讨 要提高三维模型OBJ格式轻量化压缩处理的效率&#xff0c;可以采取以下方法&#xff1a; 1、优化算法选择&#xff1a;选择合适的优化算法对模型进行轻量化处理。不同的优化算法有不同的时间复杂度和效果。一些常用的优化…

软件测试用例经典方法 | 因果图法及案例

典型的黑盒测试用例设计方法包括等价类划分法、边界值分析法、决策表法、因果图法等。 如果程序的输入条件之间相互存在联系,那么就会使情况变得复杂,因为要检查输入条件的组合情况并不是一件容易的事情,即使把所有输入条件划分为等价类,它们之间的组合情况也相当多,难以分析。…

【Go 基础篇】深入探索:Go语言中的二维数组

在计算机编程中&#xff0c;数组是一种基本的数据结构&#xff0c;用于存储相同类型的元素。而二维数组作为数组的一种扩展&#xff0c;允许我们以类似表格的方式存储和处理数据。在Go语言中&#xff0c;二维数组是一个重要的概念&#xff0c;本文将深入探讨Go语言中的二维数组…

兄弟,王者荣耀的段位排行榜是通过Redis实现的?

目录 一、排行榜设计方案1、数据库直接排序2、王者荣耀好友排行 二、Redis实现计数器1、什么是计数器功能&#xff1f;2、Redis实现计数器的原理&#xff08;1&#xff09;使用INCR命令实现计数器&#xff08;2&#xff09;使用INCRBY命令实现计数器 三、通过Redis实现“王者荣…

基于知识引入的情感分析研究综述

1.引文 情感分析知识 当training数据不足以覆盖inference阶段遇到的特征时&#xff0c;是标注更多的数据还是利用现有外部知识充当监督信号&#xff1f; 基于机器学习、深度学习的情感分析方法&#xff0c;经常会遇到有标注数据不足&#xff0c;在实际应用过程中泛化能力差的局…

C语言巧用联合体union判定数据的存储格式(大小端)

联合体大家可能比较陌生&#xff0c;但是大家对结构体稍微熟悉一点吧。其实它们二个类似&#xff0c;只不过结构体成员占用不同的地址&#xff0c;而联合体所有成员占用相同地址。利用这个特性我们就能判断在当前编译器下存储的数据的格式。那么如何确定呢&#xff1f; 我这里…

FPGA GTX全网最细讲解,aurora 8b/10b协议,OV5640板对板视频传输,提供2套工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、GTX 全网最细解读GTX 基本结构GTX 发送和接收处理流程GTX 的参考时钟GTX 发送接口GTX 接收接口GTX IP核调用和使用 4、设计思路框架视频源选择OV5640摄像头配置及采集动态彩条视频数据组包GTX aurora 8b/10b数据对…

Java“牵手”天猫图片识别商品信息API接口数据,图片搜索商品接口,天猫拍立淘API接口申请指南

天猫平台按图搜商品接口&#xff08;拍立淘&#xff09;是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取天猫商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片、最低价、当前价格、价格信息等详细信息 。 获取拍立淘接口API…