Redis数据结构之Set

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

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

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

相关文章

spring之Spring测试与集成

Spring测试与集成 摘要引言词汇解释详细介绍单元测试和集成测试单元测试和集成测试编写单元测试和集成测试代码单元测试示例集成测试示例 Spring Test和JUnit简介编写使用Spring Test和JUnit的测试代码UserService示例单元测试示例 使用Spring Test和JUnit 注意事项总结参考资料…

【WebSocket】前端使用WebSocket实时通信

目录 前言什么是WebSocketWebSocket的工作原理WebSocket与HTTP的关系HTTP建立持久化连接WebSocket类封装 前言 最近写项目&#xff0c;需要实现消息通知和实时聊天的功能&#xff0c;就去了解了一些关于websocket的知识&#xff0c;总结如下。 什么是WebSocket WebSocket 是一…

5.8.webrtc事件处理基础知识

在之前的课程中呢&#xff0c;我向你介绍了大量web rtc线程相关内容&#xff0c;今天呢&#xff0c;我们来看一下线程事件处理的基本知识。首先&#xff0c;我们要清楚啊&#xff0c;不同的平台处理事件的API是不一样的&#xff0c;这就如同我们当时创建线程是类似的&#xff0…

第12步---MySQL的JDBC操作

第12步---MySQL的JDBC操作 1.概述 采用Java API 的方式实现数据之间的操作。 根据不同的数据库采用了不同的驱动&#xff0c;接口是一致的。 下载的地址 MySQL :: Download MySQL Connector/J (Archived Versions) 2.执行流程 注册驱动 创建连接 执行sql语句的对象 结果…

Kaggle回归问题Mercedes——Benz Greener Manufacturing

目录 前言1 题目介绍2 数据清洗3 数据可视化分析4 模型训练5 源码 前言 这是我在大三选修课的课程设计&#xff0c;内容参考了Kaggle上高赞的代码&#xff0c;有详细批注&#xff0c;整体比较基础&#xff0c;结构相对完整&#xff0c;便于初学者学习。这个是一个回归问题&…

MAC电脑外放没有声音解决方案

烦人呐&#xff0c;我的mac外接显示屏幕&#xff0c;显示器没有音频输出&#xff0c;需要mac笔记本的音频输出&#xff0c;但是经常打开后&#xff0c;mac没有声音输出&#xff0c;需要重启电脑才能生效。亲测一下方法有效&#xff0c;请参考&#xff1a; 文章目录 一、短期方案…

17.4 【Linux】systemctl 针对 timer 的配置文件

有时候&#xff0c;某些服务你想要定期执行&#xff0c;或者是开机后执行&#xff0c;或者是什么服务启动多久后执行等等的。在过去&#xff0c;我们大概都是使用 crond 这个服务来定期处理&#xff0c; 不过&#xff0c;既然现在有一直常驻在内存当中的 systemd 这个好用的东西…

无涯教程-PHP - 常量数组

现在可以使用 define()函数定义数组常量。在PHP 5.6中&#xff0c;只能使用 const 关键字定义它们。 <?php//define a array using define functiondefine(animals, [dog,cat,bird]);print(animals[1]); ?> 它产生以下浏览器输出- cat PHP - 常量数组 - 无涯教程网无…

excel逻辑函数篇1

1、AND(logical1,[logical2],…)&#xff1a;用于测试所有条件是否均为TRUE 检查所有参数均为true&#xff0c;如果是则返回true 2、OR(logical1,[logical2],…)&#xff1a;用于测试是否有为TRUE的条件 如果任意参数值为true&#xff0c;即返回true&#xff1b;只有当所有参数…

673. 最长递增子序列的个数

673. 最长递增子序列的个数 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;方法一&#xff1a;动态规划方法二&#xff1a;贪心 前缀和 二分查找 参考代码&#xff1a;__673最长递增子序列的个数__动态规划__673最长递增子序列的个数__贪心_前缀和_二分查找…

ChatGPT 随机动态可视化图表分析

动态可视化图表分析实例如下图&#xff1a; 这样的动态可视化图表可以使用ChatGPT OpenAI 来实现。 给ChatGPT发送指令&#xff1a; 你现在是一个数据分析师&#xff0c;请使用HTML&#xff0c;JS&#xff0c;Echarts&#xff0c;来完成一个动态条形图&#xff0c;条形图方向横…

汽车电子笔记之:AUTOSA架构下的OS概述

目录 1、实时操作系统&#xff08;RTOS&#xff09; 2、OSEK操作系统 2.1、OSEK概述 2.2、OSEK处理等级 2.3、OSEK任务符合类 2.4、OSEK优先级天花板模式 3、AUTOSAR OS 3.1、 AUTOSAR OS对OSEK OS的继承和扩展 3.2、AUTOSAR OS的调度表 3.3、AUTOSAR OS的时间保护 3…

OS 内核级线程

用户级线程是两个栈&#xff0c;核心级线程是两套栈&#xff0c;用户栈和内核栈 用户级是并发&#xff08;同时触发、交替执行&#xff09;&#xff0c;这个是并行&#xff08;同时触发可以同时执行&#xff09; 进入内核的唯一方式是中断 根据TCB的切换&#xff0c;实现内核…

C++11特性详解

一、简介 在C11标准出来之前&#xff0c;一直是C98/03标准占引领地位&#xff0c;而C98/03标准是C98标准在2003年将存在的一些漏洞进行了修复&#xff0c;但并没有核心语法的改动。相比于C98/03&#xff0c;C11则带来了数量可观的变化&#xff0c;其中包含了约140个新特性&…

深度学习处理文本(NLP)

文章目录 引言1. 反向传播1.1 实例流程实现1.2 前向传播1.3 计算损失1.4 反向传播误差1.5 更新权重1.6 迭代1.7 BackPropagation & Adam 代码实例 2. 优化器 -- Adam2.1 Adam解析2.2 代码实例 3. NLP任务4. 神经网络处理文本4.1 step1 字符数值化4.2 step 2 矩阵转化为向量…

HTML基础知识点

目录 ​编辑一、使用 vscode 二、研究代码的特点 三、HTML 常见标签 注释标签 标题标签 段落标签 换行标签 格式化标签 图片标签 超链接标签 表格标签 列表标签 表单标签&#xff1a; form 标签 input标签&#xff1a; select textarea标签&#xff1a; 无语…

【stable-diffusion使用扩展+插件和模型资源(下)】

插件模型魔法图片等资源&#xff1a;https://tianfeng.space/1240.html 书接上文&#xff1a;&#xff08;上&#xff09; 插件推荐 1.lobe theme lobe theme是一款主题插件&#xff0c;直接可以在扩展安装 界面进行了重新布局&#xff0c;做了一些优化&#xff0c;有兴趣的…

HDLBits-Verilog学习记录 | Verilog Language-Vectors

文章目录 11.vectors | vector012.vectors in more detail | vector113.Vector part select | Vector214.Bitwise operators | Vectorgates15.Four-input gates | Gates416.Vector concatenation operator | Vector317.Vector reversal 1 | Vectorr18. Replication operator | …

平衡二叉树的插入和删除(从现在开始摆脱旋转)

平衡二叉树是指任意节点的左子树和右子树高度之差的绝对值不超过1 一.插入操作 1.找到合适位置插入 2.从下到上&#xff0c;沿着插入节点与根节点的连线&#xff0c;找到不平衡的二叉树 以68为根节点的二叉树平衡&#xff0c;左右子树高度差为1 以60为根节点的二叉树不平衡&a…

【Adobe After Effects】关于ae点击空格不会播放反而回退一帧的解决方案

最近玩ae的时候遇见了一个小问题&#xff0c;就是有时候敲空格&#xff0c;视频没办法播放&#xff0c;反而会回退一帧&#xff0c;经过摸索发现了一个解决办法&#xff1a; 点击编辑---首选项 然后选择“音频硬件” 然后选择正确的默认输出&#xff0c;点击确定即可