当前位置: 首页 > news >正文

redis基本数据结构-set

文章目录

    • 1. set的基本介绍
      • 1.1. set底层结构之hash表的简单介绍
      • 1.2. 常用命令
    • 2. 常见的业务场景
      • 2.1. 标签系统
      • 2.2. 社交网络好友关系

1. set的基本介绍

参考链接:https://mp.weixin.qq.com/s/srkd73bS2n3mjIADLVg72A
redis 的 set 数据结构是一个无序的集合,可以存储不重复的元素。适合于多种应用场景,尤其是在需要去重、快速查找和集合运算的场合。从上面的介绍可以看到set主要的特性为:
无序性:元素在set中没有特定的顺序;
唯一性:set中的元素是唯一的,不能重复。
其中还有一个比较重要的特性就是高效性。因为redis 的 Set 数据结构是基于哈希表(hash table)实现的,这也是为什么对元素的添加、删除和查找操作的时间复杂度都是 O(1) 的原因。

1.1. set底层结构之hash表的简单介绍

  1. 哈希函数:哈希表使用哈希函数将元素映射到一个数组的索引中。通过这个索引,我们可以快速定位到存储在该位置的元素。
  2. 存储结构:在 Redis 中,Set 存储元素时会使用一个哈希表,其键是元素的值,值通常是一个指向该元素的指针。这样,每个元素的插入、查找或删除操作都可以直接通过哈希表的索引进行。
  3. 添加元素(SADD):当添加一个新元素时,Redis 计算该元素的哈希值,并根据这个哈希值找到相应的索引位置。如果该位置为空(即没有相同的元素),就将该元素插入数组中。这一过程的时间复杂度是 O(1)。
  4. 删除元素(SREM):删除一个元素时,Redis 同样计算该元素的哈希值并找到相应的索引位置。如果该元素存在,就将其从哈希表中移除。这一过程也是 O(1)。
  5. 查找元素(SISMEMBER):查找一个元素是否在集合中,Redis 计算该元素的哈希值,并通过该哈希值定位到对应的索引位置。如果该元素存在于该位置,则返回 true;如果不存在,则返回 false。这一过程同样是 O(1)。
    理论上的限制
    尽管哈希表的平均时间复杂度是 O(1),在某些情况下,哈希表可能会出现冲突(即不同的元素计算出相同的哈希值)。当发生冲突时,多个元素可能会存储在同一个索引位置,导致查找、插入和删除的性能下降。此时,复杂度可能退化为 O(n),其中 n 是发生冲突的元素数量。然而,Redis 使用了一些策略(如动态扩容和链表/树结构来处理冲突)来保持哈希表的性能,使得在大多数情况下,操作的时间复杂度仍然可以保持在 O(1)。

1.2. 常用命令

SADD key member [member ...]:将一个或多个成员添加到集合中。
SREM key member [member ...]:移除集合中的一个或多个成员。
SISMEMBER key member:判断某个成员是否是集合中的成员。
SMEMBERS key:返回集合中的所有成员。
SCARD key:返回集合中成员的数量。
交集:SINTER key1 [key2 ...]:返回所有给定集合的交集。
并集:SUNION key1 [key2 ...]:返回所有给定集合的并集。
差集:SDIFF key1 [key2 ...]:返回第一个集合与其他集合的差集。
SADD myset "apple" "banana" "orange"
SREM myset "banana"
SISMEMBER myset "apple"  # 返回 1 (true)
SMEMBERS myset  # 返回 ["apple", "orange"]
SCARD myset  # 返回 2SADD set1 "a" "b" "c" 
SADD set2 "b" "c" "d" 
SINTER set1 set2  # 返回 ["b", "c"] 
SUNION set1 set2  # 返回 ["a", "b", "c", "d"] 
SDIFF set1 set2   # 返回 ["a"]

2. 常见的业务场景

一般业务场景的使用是会根据对应数据结构的特性来说明的:

  1. 去重:当需要存储一组唯一的用户 ID、标签或其他信息时,可以使用 Set 来自动去重。例如,存储用户的浏览历史,确保每个用户的历史记录中没有重复的页面。【唯一性】
  2. 社交网络:在社交网络应用中,可以使用 Set 来表示用户的好友关系。比如,用户 A 的好友可以存储在 Set 中,快速判断用户 B 是否是用户 A 的好友。【唯一性,高效性】
  3. 标签系统:对于文章、商品等,可以使用 Set 来存储相关标签,方便进行标签查询和管理。【高效性】
  4. 投票系统:可以使用 Set 存储已经投票的用户 ID,确保每个用户只能投一次票。【唯一性】
  5. 实时分析:在实时分析场景中,可以使用 Set 来跟踪活跃用户、访问过的页面等。【高效性】

2.1. 标签系统

标签系统:Set类型可用于存储和处理具有标签特性的数据,如商品标签、文章分类标签等。
背景
在一个内容平台上,用户可以给文章打上不同的标签,系统需要根据标签过滤和推荐文章。
在这里插入图片描述
优势

  1. 快速查找:使用Set可以快速判断一个元素是否属于某个集合。
  2. 灵活的标签管理:方便地添加和删除标签,实现标签的灵活管理。
  3. 集合运算:通过集合运算,如交集和并集,可以轻松实现复杂的标签过滤逻辑。

代码实现

package mainimport ("context""fmt""github.com/go-redis/redis/v8"
)var ctx = context.Background()// Redis 客户端初始化
var rdb = redis.NewClient(&redis.Options{Addr:     "",  // Redis 服务器地址Password: "", // 密码DB:       0,                  // 使用默认 DB
})// 文章标签管理结构体
type ArticleTagManager struct{}// 给文章添加标签
func (atm *ArticleTagManager) AddTagToArticle(articleID string, tags []string) error {_, err := rdb.SAdd(ctx, "article:"+articleID+":tags", tags).Result()if err != nil {return err}for _, tag := range tags {_, err := rdb.SAdd(ctx, "global:tags:"+tag, "article:"+articleID).Result()if err != nil {return err}}log.Printf("Added tags %v to article %s\n", tags, articleID)return nil
}// 从文章中删除标签
func (atm *ArticleTagManager) RemoveTagFromArticle(articleID string, tag string) error {_, err := rdb.SRem(ctx, "article:"+articleID+":tags", tag).Result()if err != nil {return err}log.Printf("Removed tag %s from article %s\n", tag, articleID)return nil
}// 获取文章的所有标签
func (atm *ArticleTagManager) GetTagsOfArticle(articleID string) ([]string, error) {tags, err := rdb.SMembers(ctx, "article:"+articleID+":tags").Result()if err != nil {return nil, err}return tags, nil
}// 根据标签获取文章列表
func (atm *ArticleTagManager) GetArticlesByTag(tag string) ([]string, error) {articles, err := rdb.SMembers(ctx, "global:tags:"+tag).Result()if err != nil {return nil, err}return articles, nil
}// 获取文章的标签数量
func (atm *ArticleTagManager) GetTagCountOfArticle(articleID string) (int64, error) {count, err := rdb.SCard(ctx, "article:"+articleID+":tags").Result()if err != nil {return 0, err}return count, nil
}// 获取某个标签的文章数量
func (atm *ArticleTagManager) GetArticleCountByTag(tag string) (int64, error) {count, err := rdb.SCard(ctx, "global:tags:"+tag).Result()if err != nil {return 0, err}return count, nil
}func main() {atm := &ArticleTagManager{}// 为文章添加标签if err := atm.AddTagToArticle("1", []string{"Golang", "Redis"}); err != nil {log.Fatalf("Error adding tags: %v", err)}if err := atm.AddTagToArticle("2", []string{"Python", "Redis"}); err != nil {log.Fatalf("Error adding tags: %v", err)}if err := atm.AddTagToArticle("3", []string{"Golang", "Python"}); err != nil {log.Fatalf("Error adding tags: %v", err)}// 获取文章的所有标签if tags, err := atm.GetTagsOfArticle("1"); err == nil {fmt.Println("Tags for article 1:")for _, tag := range tags {fmt.Println(tag)}}// 根据标签获取文章列表if articles, err := atm.GetArticlesByTag("Golang"); err == nil {fmt.Println("Articles with tag 'Golang':")for _, article := range articles {fmt.Println(article)}}// 获取文章标签数量if count, err := atm.GetTagCountOfArticle("1"); err == nil {fmt.Printf("Article 1 has %d tags.\n", count)}// 获取某个标签的文章数量if count, err := atm.GetArticleCountByTag("Redis"); err == nil {fmt.Printf("Tag 'Redis' has %d articles associated.\n", count)}// 测试完移除整个集合rdb.Del(ctx, "article:1:tags")rdb.Del(ctx, "article:2:tags")rdb.Del(ctx, "article:3:tags")rdb.Del(ctx, "global:tags:Golang")rdb.Del(ctx, "global:tags:Redis")rdb.Del(ctx, "global:tags:Python")
}

运行结果:
在这里插入图片描述

2.2. 社交网络好友关系

社交网络好友关系:Set类型可以表示用户的好友列表,支持快速好友关系测试和好友推荐。
背景
在一个社交网络应用中,用户可以添加和删除好友,系统需要管理用户的好友关系。
在这里插入图片描述
优势

  1. 唯一性:保证好友列表中不会有重复的好友。
  2. 快速关系测试:快速判断两个用户是否互为好友。
  3. 好友推荐:利用集合运算,如差集,推荐可能认识的好友。

解决方案
使用Redis Set类型存储用户的好友集合,实现好友关系的管理。
代码实现

package mainimport ("context""fmt""github.com/go-redis/redis/v8""log"
)var ctx = context.Background()// Redis 客户端初始化
var rdb = redis.NewClient(&redis.Options{Addr:     "",  // Redis 服务器地址Password: "", // 密码DB:       0,                  // 使用默认 DB
})// 添加好友
func addFriend(userOneID string, userTwoID string) error {_, err := rdb.SAdd(ctx, "user:"+userOneID+":friends", userTwoID).Result()if err != nil {return err}_, err = rdb.SAdd(ctx, "user:"+userTwoID+":friends", userOneID).Result()return err
}// 判断是否是好友
func isFriend(userOneID string, userTwoID string) bool {return rdb.SIsMember(ctx, "user:"+userOneID+":friends", userTwoID).Val()
}// 获取用户的好友列表
func getFriendsOfUser(userID string) ([]string, error) {friends, err := rdb.SMembers(ctx, "user:"+userID+":friends").Result()return friends, err
}// 删除好友
func removeFriend(userOneID string, userTwoID string) error {_, err := rdb.SRem(ctx, "user:"+userOneID+":friends", userTwoID).Result()if err != nil {return err}_, err = rdb.SRem(ctx, "user:"+userTwoID+":friends", userOneID).Result()return err
}// 推荐可能认识的好友
func recommendFriends(userID string) ([]string, error) {// 获取用户的好友friends, err := getFriendsOfUser(userID)if err != nil {return nil, err}// 如果没有好友,则没有推荐if len(friends) == 0 {return nil, nil}// 找到所有好友的好友,以及去掉自己的好友和自己potentialFriends := make(map[string]struct{})for _, friendID := range friends {friendFriends, err := getFriendsOfUser(friendID)if err != nil {return nil, err}for _, potentialFriend := range friendFriends {// 排除自己和直接好友if potentialFriend != userID {potentialFriends[potentialFriend] = struct{}{}}}}// 将推荐的好友转换为切片recommendations := []string{}for friend := range potentialFriends {// 确保不是已经存在的好友if !isFriend(userID, friend) {recommendations = append(recommendations, friend)}}return recommendations, nil
}func main() {// 示例:添加好友if err := addFriend("user1", "user2"); err != nil {log.Fatalf("Error adding friend: %v", err)}// 示例:检查好友关系if isFriend("user1", "user2") {fmt.Println("user1 and user2 are friends.")}// 示例:获取好友列表friends, err := getFriendsOfUser("user1")if err != nil {log.Fatalf("Error getting friends: %v", err)}fmt.Println("Friends of user1:", friends)// 示例:推荐可能认识的好友recommendations, err := recommendFriends("user1")if err != nil {log.Fatalf("Error recommending friends: %v", err)}fmt.Println("Recommended friends for user1:", recommendations)// 示例:删除好友if err := removeFriend("user1", "user2"); err != nil {log.Fatalf("Error removing friend: %v", err)}
}

注意事项:

  • 虽然Set是无序的,但Redis会保持元素的插入顺序,直到集合被重新排序。
  • Set中的元素是唯一的,任何尝试添加重复元素的操作都会无效。
  • 使用集合运算时,需要注意结果集的大小,因为它可能会影响性能。

相关文章:

redis基本数据结构-set

文章目录 1. set的基本介绍1.1. set底层结构之hash表的简单介绍1.2. 常用命令 2. 常见的业务场景2.1. 标签系统2.2. 社交网络好友关系 1. set的基本介绍 参考链接:https://mp.weixin.qq.com/s/srkd73bS2n3mjIADLVg72A redis 的 set 数据结构是一个无序的集合&#…...

Android 应用安装-提交阶段

经过前面准备、浏览、协调这些步骤,马上要进入提交阶段了。所谓提交,就是把这些安装应用的相关信息和状态都放到系统中。对于已安装普通应用,它其实分为两个步骤,先卸载旧包,再安装新包。当然,如果是新安装…...

强化学习Reinforcement Learning|Q-Learning|SARSA|DQN以及改进算法

一、强化学习RL 强化学习是机器学习的一个重要的分支,是一种有效的工具,在文献中被广泛用于解决MDP问题。在一个强化学习过程中,一个智能体只能通过和它所处的环境互动学习最优策略。特别地,智能体首先观察自己当前的状态&#xf…...

【HarmonyOS NEXT开发】如何设置水平/垂直方向的左/居中/右对齐——RelativeContainer的AlignRules设置

文章目录 【HarmonyOS NEXT开发】如何设置水平/垂直方向的左/居中/右对齐——RelativeContainer的AlignRules设置RelativeContainer 和 AlignRules 的关系AlignRules 语法详解 【HarmonyOS NEXT开发】如何设置水平/垂直方向的左/居中/右对齐——RelativeContainer的AlignRules设…...

java之认识异常

在 Java 中,异常(Exception)用于处理程序运行时出现的错误或异常情况。Java 的异常处理机制基于 try, catch, finally 和 throw 关键字。 1.异常的分类: 1.1:检查型异常(CheckedException): 定义:程序在…...

JSON处理工具类

JSON处理工具类 import org.json.JSONArray; import org.json.JSONObject;import java.util.ArrayList; import java.util.List;/*** JSON处理工具类*/ public class JsonUtils {/****将json字符串转为map* param json* return java.util.Map<java.lang.String, java.lang.O…...

2022高教社杯全国大学生数学建模竞赛C题 问题一(2) Python代码演示

目录 1.2 结合玻璃的类型,分析文物样品表面有无风化化学成分含量的统计规律数据预处理绘图热力图相关系数图百分比条形图箱线图小提琴图直方图KED图描述性统计分析偏度系数峰度系数其它统计量1.2 结合玻璃的类型,分析文物样品表面有无风化化学成分含量的统计规律 数据预处理 …...

ARACom Proxy Class API 概念

1. Proxy Class 概述 生成方式&#xff1a;Proxy Class 是从 AutoSar 元模型的服务接口描述中生成的&#xff0c;ara::com 标准化了其接口&#xff0c;AP 产品供应商的工具链会生成实现该接口的代理实现类。 命名空间&#xff1a;ara::com 期望代理相关的工件在命名空间 “pro…...

【Scala入门学习】基本数据类型和变量声明

1. 基本数据类型 scala 的基本类型有 9种&#xff1a; Byte、Char、Short、Int、Long、Float、Double、Boolean、Unit Scala中没有基本数据类型的概念&#xff0c;所有的类型都是对象。 AnyVal&#xff1a;代表所有基本类型。 AnyRef&#xff1a;代表所以引用类型&#xff…...

C#基础(13)结构体

前言 随着函数的讲解完成&#xff0c;我想你已经初步有了写一些复杂逻辑功能的能力&#xff0c;但是我们会发现其实在我们大部分实际开发情况中&#xff0c;很多我们需要写的变量可能不只有一个属性。 他可能有很多变量&#xff0c;那这时候我们如果要把这些变量集中到一个东…...

Excel图片批量插入单元格排版处理插件【图片大师】

为了方便大家在图片的插入排版的重复工作中解放出来&#xff0c;最近发布了一款批量插入图片的插件&#xff0c;欢迎大家下载&#xff0c;免费试用。 这是图片的文件夹&#xff1a; 主要功能如下: 1&#xff0c;匹配单元格名称的多张图批量插入到一个单元格 该功能支持设置图…...

应用性能优化实践(二)提升应用启动和响应速度

一、提升应用启动和响应速度的方法 1、冷启动过程简介 应用启动时&#xff0c;后台无该应用的进程&#xff0c;需要创建新的进程&#xff0c;这种启动方式叫冷启动。 2、使用异步加载 使用异步加载可以在后台线程中处理耗时操作&#xff0c;从而提升应用响应速度。 3、延迟加载…...

接口测试系列文章专题

在你眼中什么是接口 HTTP协议 什么是接口测试 接口测试之工具 fiddler工具的原理 fiddler工具界面详解 fiddler工具的基本使用 fiddler使如何对手机app进行抓包的呢 fiddler手机app抓包教程 Charles自定义接口返回的数据内容 常用接口工具postman的基本使用方式 pos…...

Unity Hub自动安装指定版本Unity的Android开发环境

Unity开发Android环境要求SDK、DNK、JDK、Gradle版本都要对才能发布APK&#xff0c;自己去配置很容易出错。Unity Hub可以自动安装指定版本Unity的Android开发环境。 1.安装国内用的UnityHub&#xff08;我这里用的3.3.2-c6&#xff09; 2.找到对应的Unity版本 3.点击【从Unit…...

从0开始学ARM

1. ARM模式和寄存器 1.1 ARM处理器工作模式 Cortex系列之前的ARM处理器工作模式一共有7种。 1.1.1 工作模式 Cortex系列的ARM处理器工作模式有8种&#xff0c;多了1个monitor模式&#xff0c;如下图所示&#xff1a; ARM之所以设计出这么多种模式出来&#xff0c;就是为了…...

每日一题——第九十四题

// SortNumInFile.cpp : 此文件包含 “main” 函数。程序执行将在此处开始并结束。 // 题目&#xff1a;将一个文本文件number.txt中的数字按照从小到大排列后&#xff0c;重新写入到该文件中&#xff0c;要求排序前和排序后都输出该文件的内容。该文件中共有20个整数&#xf…...

Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解

目录 一、Linux软件包管理器 - yum&#xff08;ubuntu用apt代替yum&#xff09;1、Linux下安装软件的方式2、认识 yum3、查找软件包4、安装软件5、如何实现本地机器和云服务器之间的文件互传 二、Linux编辑器 - vim1、vim 的基本概念2、vim 下各模式的切换3、vim 命令模式各命令…...

后续学习规划 ----含我个人的学习路线,经历及感受

目前的基础 开发相关&#xff08;最重要&#xff09; 1.Java SE 从入门到起飞 2.Java Web开发 3.苍穹外卖 以上三个是和开发相关的基础。 我是按照书写的顺序学习的&#xff0c;有需要的朋友可以参考。 计算机相关 其他的话&#xff0c;都是比较久远的了。隔得时间一年半…...

Skytower

一、安装配置靶机 下载地址: SkyTower: 1 ~ VulnHub 下载之后解压发现是VirtualBox格式的 我们下载一个VirtualBox&#xff0c;这是官网 Downloads – Oracle VirtualBox 安装到默认路径就 打开后点击注册 选择解压后的vbox文件 然后点击左上角管理 点击导出虚拟电脑&…...

成型的程序

加一个提示信息 加上python 常用的包 整个程序打包完 250M 安装 960MB matplot numpy pandas scapy pysearial 常用的包 (pyvisa)… … 啥都有 Python 解释器组件构建 要比 lua 容易的多 &#xff08;C/Rust 的组件库)...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

鸿蒙HarmonyOS 5军旗小游戏实现指南

1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;采用DevEco Studio实现&#xff0c;包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...

【笔记】AI Agent 项目 SUNA 部署 之 Docker 构建记录

#工作记录 构建过程记录 Microsoft Windows [Version 10.0.27871.1000] (c) Microsoft Corporation. All rights reserved.(suna-py3.12) F:\PythonProjects\suna>python setup.py --admin███████╗██╗ ██╗███╗ ██╗ █████╗ ██╔════╝…...

Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用

Linux 内存管理调试分析&#xff1a;ftrace、perf、crash 的系统化使用 Linux 内核内存管理是构成整个内核性能和系统稳定性的基础&#xff0c;但这一子系统结构复杂&#xff0c;常常有设置失败、性能展示不良、OOM 杀进程等问题。要分析这些问题&#xff0c;需要一套工具化、…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】

1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...

后端下载限速(redis记录实时并发,bucket4j动态限速)

✅ 使用 Redis 记录 所有用户的实时并发下载数✅ 使用 Bucket4j 实现 全局下载速率限制&#xff08;动态&#xff09;✅ 支持 动态调整限速策略✅ 下载接口安全、稳定、可监控 &#x1f9e9; 整体架构概览 模块功能Redis存储全局并发数和带宽令牌桶状态Bucket4j Redis分布式限…...

JS设计模式(5): 发布订阅模式

解锁JavaScript发布订阅模式&#xff1a;让代码沟通更优雅 在JavaScript的世界里&#xff0c;我们常常会遇到这样的场景&#xff1a;多个模块之间需要相互通信&#xff0c;但是又不想让它们产生过于紧密的耦合。这时候&#xff0c;发布订阅模式就像一位优雅的信使&#xff0c;…...

【汇编逆向系列】四、函数调用包含单个参数之Double类型-mmword,movsd,mulsd,addsd指令,总结汇编的数据类型

一、汇编代码 上一节开始&#xff0c;讲到了很多debug编译独有的汇编方式&#xff0c;为了更好的区分release的编译器优化和debug的区别&#xff0c;从本章节开始将会提供debug和release的汇编用作对比 Debugb编译 single_double_param:00000000000000A0: F2 0F 11 44 24 08…...