《程序猿之Redis缓存实战 · 集合类型》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 集合(Set)

集合(Set)

【简介】

Redis 中的 Set 类型是一种无序集合,集合中的元素唯一,也就是集合中的元素是无重复的,有点类似于 Java 中的 HashSet 。

【应用场景】

1、需要随机获取数据源中的元素的场景

● 举例 :抽奖系统、随机。

● 相关命令:SADD(加入抽奖系统)、SMEMBERS(查看所有抽奖用户)、SPOP(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、SRANDMEMBER(随机获取集合中的元素,适合允许重复中奖的场景)。

2、需要存放的数据不能重复的场景

● 举例:文章点赞、动态点赞等场景。

● 相关命令:SADD(点赞)、SREM(移除点赞)、SISMEMBER(检查用户是否点赞过)、SMEMBERS(获取点赞用户列表)、SCARD(获取点赞用户数量)

Tips:理论上,HashSet 适合的存储场景,这边也适合了。

【应用场景补充】

Redis Set 集合是一种无序且不重复的元素集合,适用于多种应用场景。

1、去重存储:使用 Set 存储唯一的用户 ID、邮箱地址等,确保没有重复数据。

2、标签系统:为用户、文章等对象存储标签,方便快速检索和去重。

3、社交网络:存储用户的好友列表、关注者、粉丝等,确保每个用户的关系是唯一的。

4、推荐系统:存储用户的历史行为、偏好等,便于进行个性化推荐。

5、实时统计:统计在线用户、活跃用户等,使用 Set 可以快速判断用户是否在线或活跃。

6、权限管理:存储用户的角色或权限,确保每个角色或权限是唯一的。

7、交集、并集、差集操作:Redis 提供了对 Set 的集合运算支持,可以方便地进行交集、并集和差集操作。

总之,Redis Set 集合在处理唯一性和集合操作方面非常高效,适用于多种场景。在使用时,需注意内存使用、数据持久化和操作性能等问题,以确保系统的稳定性和高效性。

@Component
public class RedisExample implements CommandLineRunner {@Autowiredprivate UserService userService;@Autowiredprivate TagService tagService;@Autowiredprivate SetOperationsService setOperationsService;@Overridepublic void run(String... args) throws Exception {// 去重存储userService.addUserId("user1");userService.addUserId("user2");userService.addUserId("user1"); // 重复的 ID 不会被添加System.out.println("去重存储的用户 ID: " + userService.getUserIds());// 标签系统tagService.addTag("python");tagService.addTag("coding");tagService.addTag("python"); // 重复的标签不会被添加System.out.println("标签系统中的标签: " + tagService.getTags());// 集合操作示例redisTemplate.opsForSet().add("setA", "1", "2", "3");redisTemplate.opsForSet().add("setB", "3", "4", "5");System.out.println("交集: " + setOperationsService.getIntersection("setA", "setB"));System.out.println("并集: " + setOperationsService.getUnion("setA", "setB"));System.out.println("差集: " + setOperationsService.getDifference("setA", "setB"));}
}@Service
public class UserService {@Autowiredprivate RedisTemplate<String, String> redisTemplate;public void addUserId(String userId) {redisTemplate.opsForSet().add("user_ids", userId);}public Set<String> getUserIds() {return redisTemplate.opsForSet().members("user_ids");}
}@Service
public class TagService {@Autowiredprivate RedisTemplate<String, String> redisTemplate;public void addTag(String tag) {redisTemplate.opsForSet().add("tags", tag);}public Set<String> getTags() {return redisTemplate.opsForSet().members("tags");}
}@Service
public class SetOperationsService {@Autowiredprivate RedisTemplate<String, String> redisTemplate;public Set<String> getIntersection(String setKey1, String setKey2) {return redisTemplate.opsForSet().intersect(setKey1, setKey2);}public Set<String> getUnion(String setKey1, String setKey2) {return redisTemplate.opsForSet().union(setKey1, setKey2);}public Set<String> getDifference(String setKey1, String setKey2) {return redisTemplate.opsForSet().difference(setKey1, setKey2);}
}

【注意事项】

  1. 数据类型: Set 集合只能存储字符串类型的元素,如果需要存储其他类型的数据,需要进行序列化和反序列化。
  2. 元素数量: Set 集合的元素数量有限制,具体取决于 Redis 实例的配置。
  3. 性能: Set 集合的添加、删除、判断元素是否存在等操作的性能都非常高,但如果元素数量非常多,可能会影响性能。
  4. 数据持久化: Set 集合的数据默认存储在内存中,如果需要持久化数据,需要配置 Redis 的持久化机制。
  5. 并发访问: Set 集合支持并发访问,但需要考虑并发访问带来的数据一致性问题。

【基础操作】

SADD key member1 [ member2 ]:向集合添加一个或者多个成员
SMEBERS key:返回集合中的所有成员
SISMEMBER key member:判断menber元素是否是集合key的成员
SPOP key:移除并返回集合中的一个随机元素
SREM key member1 [ member2 ]:移除集合中一个或者多个成员
● SADD key member1 [ member2 ]:向集合添加一个或者多个成员
● SCARD key:获取集合的成员数
● SMEBERS key:返回集合中的所有成员
● SPOP key:移除并返回集合中的一个随机元素(用户抽奖只能参加一轮)
● SRANDMEMBER key [ count ]:返回集合中一个或者多个随机数(用户抽奖可以参加多轮)
● SREM key member1 [ member2 ]:移除集合中一个或者多个成员---------------------------------------------------分隔线---------------------------------------------------
● SUNION key1 [ key2 ]:返回所有给定集合的并集
● SINTER key1 [ key2 ]:返回所有给定集合的交集
● SDIFF key1 [ key2 ]:返回给定所有集合的差集
● SUNIONSTORE destination key1 [ key2 ]:所有给定集合的并集存储在destination集合中
● SDIFFSTORE destination key1 [ key2 ]:返回给定所有集合的差集并存储在destination中
● SINTERSTORE destination key1 [ key2 ]:返回给定所有集合的交集并存储在destination中
● SISMEMBER key member:判断menber元素是否是集合key的成员
● SMOVE source destination menber:将member元素从source集合移动到destination集合
● SSCAN key cursor [ MATCH pattem ] [ COUNT count ]:迭代集合中的元素

【代码实战】

命令和代码方法,大部分都是去掉首字母,比如set的sadd,就是add。

当然少部分换了单词。

=== 抽奖:

=== 点赞:

【扩展:实战文章点赞】

前言,下方位图结构也说适合点赞,那么哪个更适合呢?

这里补充一点,使用Set处理,还可以不一定用户ID一定是Long类型。

Tips:哈哈,这么一看,新增加的三个高级数据结构,貌似使用场景相当少。

对于文章点赞功能,使用 Redis 的 Set 结构更适合。=== Set 结构的优势:
去重: Set 结构天生具有去重功能,可以确保每个用户只点赞一次。
高效查询: 可以使用 SISMEMBER 命令快速判断用户是否点赞过。
高效添加和删除: 可以使用 SADD 和 SREM 命令高效地添加和删除点赞记录。
获取点赞总数: 可以使用 SCARD 命令获取点赞总数。=== 位图结构的优势:
节省空间: 位图结构可以高效地存储大量布尔值,节省存储空间。
高效统计: 可以使用 BITCOUNT 命令快速统计点赞总数。=== 为什么 Set 结构更适合:
点赞功能更关注用户是否点赞,而不是点赞总数: Set 结构更适合存储用户点赞状态,而位图结构更适合统计点赞总数。
Set 结构更灵活: 可以使用 Set 结构存储其他信息,例如点赞时间、点赞类型等。
Set 结构更易于扩展: 可以使用 Set 结构实现更复杂的点赞功能,例如取消点赞、点赞排行榜等。=== 总结:
对于文章点赞功能,使用 Redis 的 Set 结构更适合,因为它更灵活、更易于扩展,并且可以满足点赞功能的基本需求。
@Service
public class ArticleLikeService {private static final String ARTICLE_LIKES_PREFIX = "article:likes:";@Autowiredprivate RedisTemplate<String, String> redisTemplate;// 点赞public void likeArticle(String articleId, String userId) {String key = ARTICLE_LIKES_PREFIX + articleId;redisTemplate.opsForSet().add(key, userId);}// 取消点赞public void unlikeArticle(String articleId, String userId) {String key = ARTICLE_LIKES_PREFIX + articleId;redisTemplate.opsForSet().remove(key, userId);}// 获取点赞用户列表public Set<String> getLikes(String articleId) {String key = ARTICLE_LIKES_PREFIX + articleId;return redisTemplate.opsForSet().members(key);}// 获取点赞总数public Long getLikeCount(String articleId) {String key = ARTICLE_LIKES_PREFIX + articleId;return redisTemplate.opsForSet().size(key);}
}

【扩展:点赞想记录点赞时间】

Gpt给了两个方案,第一种还是用Set,但是存储的是Map。

@Service
public class ArticleLikeService {private final RedisTemplate<String, Object> redisTemplate;public ArticleLikeService(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}// 添加点赞public void addLike(Long articleId, String userId) {String key = "article:likes:" + articleId;Map<String, Object> likeData = new HashMap<>();likeData.put("userId", userId);likeData.put("timestamp", Instant.now().toEpochMilli());redisTemplate.opsForSet().add(key, likeData);}// 取消点赞public void removeLike(Long articleId, String userId) {String key = "article:likes:" + articleId;redisTemplate.opsForSet().remove(key, userId);}// 判断用户是否点赞public boolean isLiked(Long articleId, String userId) {String key = "article:likes:" + articleId;return redisTemplate.opsForSet().isMember(key, userId);}// 获取点赞总数public Long getLikeCount(Long articleId) {String key = "article:likes:" + articleId;return redisTemplate.opsForSet().size(key);}// 获取点赞时间public Long getLikeTimestamp(Long articleId, String userId) {String key = "article:likes:" + articleId;Map<String, Object> likeData = (Map<String, Object>) redisTemplate.opsForSet().member(key, userId);if (likeData != null) {return (Long) likeData.get("timestamp");}return null;}
}

继续追问:redisTemplate.opsForSet()的值可以直接存储Map对象吗?是会自动转换吗?那是根据什么判断重复,hashCode吗?

你问的很好!RedisTemplate 的 opsForSet() 方法确实可以存储 Map 对象,但它不会自动进行转换。Redis 的 Set 数据结构只能存储字符串类型的元素。,因此,如果你直接将 Map 对象放入 opsForSet() 方法,Redis 会将它序列化成字符串,然后存储。判断重复的依据是字符串的比较。 Redis 会使用字符串比较来判断两个元素是否重复,而不是使用 hashCode。解决方法:
1、手动序列化和反序列化: 你需要在存储 Map 对象之前将其序列化成字符串,并在读取时将其反序列化回 Map 对象。可以使用 Jackson 或 Gson 等序列化库来完成这个过程。
2、使用 opsForHash() 方法: 如果需要存储 Map 对象,建议使用 opsForHash() 方法,它可以存储键值对,并支持直接存储 Map 对象。

总结一下,这种情况还是直接使用Hash处理好了,当然,也可以使用 ZSet 来存储点赞记录,并使用点赞时间作为分数,这样可以方便地获取点赞时间排序的列表。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「OC」探索 KVC 的基础与应用

「OC」KVC的初步学习 文章目录 「OC」KVC的初步学习前言介绍KVC的相关方法key和keyPath的区别KVC的工作原理KVO的setValue:forKey原理KVO的ValueforKey原理 在集合之中KVC的用法1. mutableArrayValueForKey: 和 mutableArrayValueForKeyPath:2. mutableSetValueForKey: 和 muta…

Java项目实战II基于Java+Spring Boot+MySQL的智能物流管理系统(文档+源码+数据库)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 随着电子商务的蓬勃发展&#xff0c;物流行业迎来了前所未有的挑战与机遇。传统物流管理方式在应对海…

PDCA优化任务流程

这里写目录标题 一、背景二、PDCA重要性与必要性概念PDCA循环的重要性 三、PDCA分析这次任务的执行任务描述分析原因&#xff1a;结合PDCA分析&#xff1a;提高办法&#xff1a; 四、总结 一、背景 汇报任务完成情况&#xff0c;未提交实际成果。 本次总结旨在通过PDCA循环的视…

二值图像的面积求取的两种方法及MATLAB实现

一、引言 面积在数字图像处理中经常用到&#xff0c;在MATLAB中&#xff0c;计算二值图像的面积通常可以通过两种主要方法实现&#xff1a;遍历法和直接利用bwarea函数。下面将分别介绍这两种方法的原理和相应的MATLAB代码示例。 二、遍历法计算二值图像面积的原理和MATLAB代码…

如何创建虚拟环境并实现目标检测及验证能否GPU加速

创建虚拟环境&#xff1a; 先创建一个虚拟python环境&#xff0c;敲如下代码 然后再到该虚拟环境里面安装自己想要的包 激活虚拟环境 然后再聚类训练这些 验证GPU加速 阿里源 pip install torch torchvision -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mir…

Appinventor2 多屏幕之间如何共享过程?

先说结论&#xff1a;不能共享&#xff0c;但可以变通&#xff0c;这个问题上没有完美方案&#xff01; Appinventor2 多屏幕之间如何共享过程&#xff1f;或者说如何跨屏幕调用其他屏幕的过程&#xff1f; 相信有很多人有过这样的问题&#xff0c;但是目前来看每个屏幕都是独…

Linux下驱动开发实例

驱动开发 驱动与硬件的分离 在传统的嵌入式系统开发中&#xff0c;硬件信息往往是直接硬编码在驱动代码中的。这样做的问题是&#xff0c;当硬件发生变化时&#xff0c;比如增加或更换设备&#xff0c;就需要修改驱动程序的代码&#xff0c;这会导致维护成本非常高。因此&…

机器学习周报(9.23-9.29)

文章目录 摘要Abstract1 自监督学习&#xff08;Self-Supervised Learning&#xff09;1.1 BERT1.1.1 Masking Input1.1.2 Next Sentence Prediction1.1.3 BERT的使用方式 1.2 Why does BERT work?1.3 Multi-lingual BERT 2 pytorch中tensor相关函数学习使用2.1 张量拼接与拆分…

4G模组SIM卡电路很简单,但也要注意这些坑

上次水SIM卡相关的文章&#xff0c;还是上一次&#xff1b; 上一篇文章里吹牛说&#xff0c;跟SIM卡相关的问题还有很多&#xff0c;目的是为下一篇文章埋下伏笔&#xff1b;伏笔埋是埋下了&#xff0c;但如果债老是不还&#xff0c;心里的石头就总悬着&#xff0c;搞不好老板…

MAC的几个常见的快捷方式

1.mac 查看图片好的方式 默认查看图片的方式无法直接切换上一张下一张 解决方法&#xff1a; 1.&#xff08;最好的方法&#xff09;选中图片直接按空格&#xff0c;进入快速预览图片 2.就是全部选中然后打开&#xff0c;但是说实话有点奇怪&#xff0c;而且很占内存 3.直接显示…

Linux 信号捕捉

我们知道信号的处理不是即时的&#xff0c;进程在合适的时机才会处理信号&#xff0c;而这个时机就比如从内核态返回用户态。 1. 用户态与内核态 在操作系统中&#xff0c;用户态&#xff08;User Mode&#xff09;和内核态&#xff08;Kernel Mode&#xff09;是两种不同的C…

安卓主板_MTK4G/5G音视频记录仪整机及方案定制

音视频记录仪方案&#xff0c;采用联发科MT6877平台八核2* A78 6* A55主频高达2.4GHz, 具有高能低耗特性&#xff0c;搭载Android 12.0智能操作系统&#xff0c;可选4GB32GB/6GB128GB内存&#xff0c;运行流畅。主板集成NFC、双摄像头、防抖以及多种无线数据连接&#xff0c;支…

如何在 Kubernetes 上部署和配置开源数据集成平台 Airbyte?

在 Kubernetes 上部署和配置 Airbyte 是一个复杂但非常有价值的过程&#xff0c;特别是对于需要强大数据集成和数据处理能力的企业或团队。Airbyte 是一个开源的数据集成平台&#xff0c;允许用户从各种来源提取数据并加载到目标存储中。其强大的插件系统支持多种数据源与目标&…

新能源汽车储充机器人:能源高效与智能调度

新能源汽车储充机器人&#xff1a;开启能源高效利用与智能调度的未来之门 随着全球能源危机的日益加剧和环境污染问题的不断恶化&#xff0c;新能源汽车成为了未来交通领域的重要发展方向。然而&#xff0c;新能源汽车的普及不仅需要解决电池技术的瓶颈&#xff0c;还需要构建一…

labview更换操作系统后打开原VI闪退

labview更换操作系统后打开原VI闪退 问题描述&#xff1a; Windows11由家庭版更换为专业版后&#xff0c;重新安装labview2021&#xff0c;打开原来的项目&#xff0c;项目管理器可以正常打开&#xff0c;但是打开VI却闪退&#xff0c;并报错如下 出现这种原因主要是labview在…

通信工程学习:什么是LAN局域网、MAN城域网、WAN广域网

LAN局域网、MAN城域网、WAN广域网 LAN&#xff08;Local Area Network&#xff0c;局域网&#xff09;、MAN&#xff08;Metropolitan Area Network&#xff0c;城域网&#xff09;和WAN&#xff08;Wide Area Network&#xff0c;广域网&#xff09;是计算机网络中根据覆盖范围…

vue组合式api

一、ref&#xff08;基本类型数据&#xff0c;对象类型数据&#xff09; 1.作用&#xff1a;定义响应式变量 2.语法&#xff1a;let xxx ref(初始值) 3.返回值&#xff1a;一个RefImpl的实例对象&#xff0c;简称ref对象&#xff0c;ref对象的value属性是响应式的。 4.注意…

【MySQL】视图、用户和权限管理

目录 视图创建视图数据修改影响删除视图视图优点 用户和权限管理查看当前的数据库拥有用户信息创建用户修改密码删除用户权限授权回收权限 视图 视图就是相当于创建一个表&#xff0c;将查询到的结果集给存储起来。像使用复杂的多表查询查询到的结果集就不可以对结果集操作。而…

2024/9/29周报

文章目录 摘要Abstract污水处理工艺流程整体介绍粗格栅细格栅曝气沉砂池提升泵房峰谷平策略 初沉池&#xff08;一级处理&#xff09;工作原理运行管理 氧化沟生化池&#xff08;二级处理&#xff09;二沉池工作原理运行参数 高效沉淀池功能与特点工作原理 深度处理&#xff08…

Coursera_ Algorithms I 学习笔记:Module_3_Analysis_of_Algorithm_Introduction

Coursera_ Algorithms I 学习笔记&#xff1a;Module_3_Analysis_of_Algorithm_Introduction scientific method applied to analysis of algorithms data analysis log-log plot doubling hypothesis experimental alogrithmics System independent effectsSystem dependen…