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

Redis - 集合 Set 及代码实战

Set 类型

  1. 定义:类似 Java 中的 HashSet 类,key 是 set 的名字,value 是集合中的值
  2. 特点
    1. 无序
    2. 元素唯一
    3. 查找速度快
    4. 支持交集、并集、补集功能

常见命令

命令功能
SADD key member …添加元素
SREM key member …删除元素
SCARD key获取元素个数
SISMEMBER key member判断一个元素是否存在于 set 中
SMEMBERS获取 set 中所有元素
SINTER key1 key2 …求 key1 和 key2 集合的交集
SDIFF key1 key2 …求 key1 和 key2 集合的差集
SUNION key1 key2 ….求 key1 和 key2 集合的并集

编码方式

  1. IntSet 编码
    1. 定义:IntSet 是一个有序的整数数组结构,相比哈希表占用更少的内存空间
    2. 使用条件:当集合中存储的所有数据都是整数,并且元素数量不超过配置项 set-max-intset-entries(默认值为512)
    3. 功能:满足使用条件时 Redis 自动使用 IntSet 编码,减少内存占用
  2. HT(Hash Table)编码
    1. 定义:key 存储集合的元素,value 统一设置为 null(因为 Set 只关心元素是否存在,不需要存储值)
    2. 使用条件:当不满足 IntSet 编码条件时,Redis 会使用哈希表来存储集合
    3. 功能:提供快速的查找性能,但需要消耗更多内存


示例

  1. 目标:用户关注与取关博主,查看用户与博主的共同关注
  2. 注意:此处代码实现涉及较多 SpringBoot 和 MybatisPlus 相关知识,已默认读者有一定基础

功能点

  1. 判断当前登录用户是否已经关注当前博主
  2. 当前用户关注 & 取关当前博主
  3. 查询当前用户与当前博主的共同关注

业务方案

Set 类型(Redis)

  1. 功能:记录当前用户关注的所有博主,并且可以查看共同关注(交集操作)

  2. 数据结构 :Set

    keyvalue (set)
    follow:userId(prefix + 做出关注行为的用户 id)被 key 用户关注的用户 id 集合

MySQL

  1. 功能

    1. 记录所有关注与被关注关系:创建一个关注表,每个条目对应一个关注关系
    2. 查询是否关注:select * from subscribe_table where user_id = userId and follow_user_id followUserId (查询结果不为空则已关注)
    3. 查询关注列表:select follow_user_id from subscribe_table where user_id = userId (查询结果是所有 userId 关注的博主的id)
  2. 数据结构

    字段名功能
    idprimary key (自增)
    user_id做出关注行为的用户的 ID
    follow_user_id被关注的用户的 ID
    create_time创建时间

最终方案

  1. 利用 Redis Set 的快速查询某个用户是否已经关注另一个用户
  2. 利用 Redis Set 的交集操作快速实现共同关注功能⁠
  3. 利用 MySQL 的 follow 表完整记录并持久化所有关注与被关注的关系⁠⁠
  4. 使用 MySQL 存储关注关系的基础数据,并使用Redis Set来提升共同关注等高频查询场景的性能⁠

代码实现

  1. 配置文件

    1. 目标:自动移除非活跃用户的关注列表,下次访问时再通过 MySQL 重建缓存
    2. 方案:使用 LRU(Least Recently Used)缓存淘汰策略。当内存超出阈值时,自动淘汰最久未使用的数据
    3. 注意:需要为 follow 缓存设置独立的 key 前缀,并结合 maxmemory-policy 配置分区缓存策略,避免误删其他缓存数据
    maxmemory-policy allkeys-lru
    
  2. 实体类 Follow:

    @Data
    @TableName("follow")
    public class Follow {@TableId(type = IdType.AUTO)private Long id;private Long userId;private Long followUserId;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;
    }
    
  3. Controller

    @RestController
    @RequestMapping("/follow")
    public class FollowController {@Resourceprivate IFollowService followService;@GetMapping("/isFollow/{followUserId}")public Result isFollow(@PathVariable Long followUserId) {boolean isFollow = followService.isFollow(followUserId);return Result.ok(isFollow);}@PostMapping("/follow/{followUserId}")public Result follow(@PathVariable Long followUserId) {boolean followExecuted = followService.follow(followUserId, isFollow);return Result.ok(followExecuted);}@GetMapping("/commons/{targetUserId}")public Result followCommons(@PathVariable Long targetUserId) {List<UserDTO> commons = followService.followCommons(targetUserId);return Result.ok(commons);}
    }
    
  4. Service接口:

    public interface IFollowService extends IService<Follow> {Boolean isFollow(Long followUserId);Boolean follow(Long followUserId);List<UserDTO> followCommons(Long id);
    }
    
  5. ServiceImpl 类:

    @Service
    public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate IUserService userService;@Overridepublic Boolean isFollow(Long followUserId) {// 获取当前用户idLong userId = UserHolder.getUser().getId();String key = "follow:" + userId;// 缓存不为空,则直接查询用户关注列表if (stringRedisTemplate.hasKey(key)) {return stringRedisTemplate.opsForSet().isMember(key, followUserId.toString());}// 缓存为空时,从数据库加载用户关注列表List<Long> followIds = baseMapper.selectFollowedIds(userId);// 没有关注的博主,则缓存空对象(防止缓存穿透)if (followIds.isEmpty()) {stringRedisTemplate.opsForSet().add(key, "null");        // 缓存空对象stringRedisTemplate.expire(key, 10, TimeUnit.MINUTES);   // 设置失效时间return false;}// followIds.forEach(id -> stringRedisTemplate.opsForSet().add(key, id.toString()));stringRedisTemplate.opsForSet().add(key, followIds.stream().map(String::valueOf).toArray(String[]::new));stringRedisTemplate.expire(key, 60, TimeUnit.MINUTES);        // 设置失效时间return stringRedisTemplate.opsForSet().isMember(key, followUserId.toString());}@Overridepublic Boolean follow(Long followUserId) {Long userId = UserHolder.getUser().getId();Boolean isFollowed = isFollow(followUserId);Boolean success = false;if (!isFollowed) {// 未关注 => 关注操作Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);success = save(follow);if (success) {stringRedisTemplate.opsForSet().add(key, followUserId.toString());}} else {// 已关注 => 取关操作success = remove(new QueryWrapper<Follow>().eq("user_id", userId).eq("follow_user_id", followUserId));if (success) {stringRedisTemplate.opsForSet().remove(key, followUserId.toString());}}return success;}@Overridepublic List<UserDTO> followCommons(Long targetUserId) {Long userId = UserHolder.getUser().getId();String key1 = "follow:" + userId;String key2 = "follow:" + targetUserId;// 求交集Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key1, key2);if (intersect == null || intersect.isEmpty()) {return Collections.emptyList();}// 解析idList<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());// 查询用户List<UserDTO> users = userService.listByIds(ids).stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());return users;}
    }
    
  6. FollowMapper

    @Mapper
    public interface FollowMapper extends BaseMapper<Follow> {// 注解方式@Select("select follow_user_id from follow where user_id = #{userId}")List<Long> selectFollowedIds(Long userId);}
    

http://www.lryc.cn/news/503876.html

相关文章:

  • LabVIEW面向对象编程有什么特点?
  • 配置Nginx自签名SSL证书,支持HTTPS
  • 使用Spring Boot、VUE实现SSE长连接:跟踪文件上传和任务进度
  • 计算机网络技术基础:3.计算机网络的拓扑结构
  • go-zero(十二)消息队列
  • 会议通知:人工智能通识教育与实践发展暨和鲸科技AI通识课解决方案发布会
  • UDS自动化测试-Service 0x27(CAPL调用dll实现key计算)
  • 订单编号如何实现
  • Vue3 大事件管理系统
  • IOS通过WDA自动化中遇到的问题
  • 单独测试 pyautogui 的鼠标点击功能,确保它能够在当前环境中正常工作,鼠标自动点击的录制回放功能
  • 路由引入问题(双点双向路由回馈问题)
  • 设计模式之 适配器模式 C# 范例
  • LabVIEW实现GPS通信
  • [leetcode100] 543. 二叉树的直径
  • 嵌入式学习(18)-stm32F407串口接收空闲中断+DMA
  • b站视频爬虫-词云分析
  • 如何防止订单二次重复支付?
  • LeetCode 24反转链表
  • 用python的flask写的一个MQTT中转功能,http的方式发送数据和接收数据
  • img引入svg如何修改颜色
  • 计算机毕业设计PySpark+PyFlink+Hive地震预测系统 地震数据分析可视化 地震爬虫 大数据毕业设计 Hadoop 机器学习 深度学习
  • 【Python】使用Numpy实现余弦相似度计算
  • nginx中的root和alias的区别
  • 探索Telnet:实现Windows远程登录Ubuntu的实践指南
  • 在 Vue 2 中隐藏页面元素的方法
  • 【Java】Java8的4个函数式接口简单教程
  • 计算机组成原理与系统结构——微程序控制
  • 【Swift】集合类型 - 数组、集合、字典
  • 3D 视觉定位技术:汽车零部件制造的智能变革引擎