黑马点评-Redis Set-实现关注、取关和共同关注

发布时间:2026/7/5 3:49:45
黑马点评-Redis Set-实现关注、取关和共同关注 Redis Set 实现关注、取关和共同关注黑马点评第 9 章前半部分学习笔记本文整理自黑马点评 Redis 实战篇第 9 章前半部分。第 9 章的主题是「好友关注」前半部分主要解决两个问题用户如何关注/取关另一个用户以及如何用 Redis Set 快速查询共同关注。1. 这一篇解决什么问题黑马点评里有一个很常见的社交功能我可以关注一个博主 我也可以取消关注 进入某个博主主页时可以看到我和他都关注了哪些人。这里其实有两层问题第一层关注关系怎么存 第二层共同关注怎么快速查第 9.1 小节解决第一层用 MySQL 的tb_follow表保存关注关系。第 9.2 小节解决第二层用 Redis Set 保存每个用户关注的人然后通过交集查询共同关注。2. 关注关系本质上是什么关注关系不是用户和博客的关系而是用户和用户的关系。比如用户 5 关注了用户 10 用户 6 关注了用户 10 用户 5 关注了用户 20这说明用户 10 有两个粉丝5 和 6 用户 5 关注了两个人10 和 20项目中用tb_follow表表示这种关系对应实体类是TableName(tb_follow)publicclassFollowimplementsSerializable{TableId(valueid,typeIdType.AUTO)privateLongid;privateLonguserId;privateLongfollowUserId;privateLocalDateTimecreateTime;}最重要的是这两个字段userId 发起关注的人也就是粉丝 followUserId 被关注的人也就是博主如果数据库里有一条记录user_id 5 follow_user_id 10意思是用户 5 关注了用户 10。这个方向非常重要后面 Feed 流推送时也会依赖它。3. 关注和取关接口Controller 入口PutMapping(/{id}/{isFollow})publicResultfollow(PathVariable(id)LongfollowUserId,PathVariable(isFollow)BooleanisFollow){returnfollowService.follow(followUserId,isFollow);}请求示例PUT /follow/10/true表示当前登录用户关注用户 10。PUT /follow/10/false表示当前登录用户取消关注用户 10。这里没有让前端传当前用户 id因为当前用户应该由服务端从登录上下文中获取LonguserIdUserHolder.getUser().getId();这样可以避免用户伪造请求替别人关注或取关。4. 关注和取关的核心代码Service 实现OverridepublicResultfollow(LongfollowUserId,BooleanisFollow){LonguserIdUserHolder.getUser().getId();Stringkeyfollows:userId;if(isFollow){FollowfollownewFollow();follow.setUserId(userId);follow.setFollowUserId(followUserId);booleanisSuccesssave(follow);if(isSuccess){stringRedisTemplate.opsForSet().add(key,followUserId.toString());}}else{booleanisSuccessremove(newQueryWrapperFollow().eq(user_id,userId).eq(follow_user_id,followUserId));if(isSuccess){stringRedisTemplate.opsForSet().remove(key,followUserId.toString());}}returnResult.ok();}先看 MySQL 部分。关注时FollowfollownewFollow();follow.setUserId(userId);follow.setFollowUserId(followUserId);save(follow);等价于INSERTINTOtb_follow(user_id,follow_user_id)VALUES(当前登录用户id,被关注用户id);取关时remove(newQueryWrapperFollow().eq(user_id,userId).eq(follow_user_id,followUserId));等价于DELETEFROMtb_followWHEREuser_id当前登录用户idANDfollow_user_id被关注用户id;所以关注功能本质上很简单关注新增一条关系记录 取关删除一条关系记录5. 判断是否关注进入博主主页时前端需要知道当前用户是否已经关注了这个博主从而决定按钮显示「关注」还是「已关注」。ControllerGetMapping(/or/not/{id})publicResultisFollow(PathVariable(id)LongfollowUserId){returnfollowService.isFollow(followUserId);}ServiceOverridepublicResultisFollow(LongfollowUserId){LonguserIdUserHolder.getUser().getId();Integercountquery().eq(user_id,userId).eq(follow_user_id,followUserId).count();returnResult.ok(count0);}它对应的 SQL 是SELECTCOUNT(*)FROMtb_followWHEREuser_id当前登录用户idANDfollow_user_id被查看的用户id;如果数量大于 0说明已经关注。6. 为什么 9.2 要引入 Redis Set9.1 只用 MySQL 就可以完成关注、取关、判断是否关注。但 9.2 要做「共同关注」。假设当前用户是 5用户 5 关注了10, 20, 30现在打开用户 8 的主页用户 8 关注了20, 30, 40那么共同关注就是20, 30这本质上是集合交集{10, 20, 30} ∩ {20, 30, 40} {20, 30}Redis Set 天然适合这个场景因为它支持交集运算。7. Redis 中怎么保存关注集合每个用户维护一个 Setkey: follows:{userId} value: 该用户关注的所有用户 id比如follows:5 {10, 20, 30} follows:8 {20, 30, 40}关注成功时stringRedisTemplate.opsForSet().add(key,followUserId.toString());对应 Redis 命令SADD follows:5 10取关成功时stringRedisTemplate.opsForSet().remove(key,followUserId.toString());对应 Redis 命令SREM follows:5 10这里 MySQL 是真实数据源Redis Set 是为了共同关注查询做的加速结构。8. 共同关注接口ControllerGetMapping(/common/{id})publicResultfollowCommons(PathVariable(id)Longid){returnfollowService.followCommons(id);}请求示例GET /follow/common/8意思是查询当前登录用户和用户 8 的共同关注。ServiceOverridepublicResultfollowCommons(Longid){LonguserIdUserHolder.getUser().getId();Stringkeyfollows:userId;Stringkey2follows:id;SetStringintersectstringRedisTemplate.opsForSet().intersect(key,key2);if(intersectnull||intersect.isEmpty()){returnResult.ok(Collections.emptyList());}ListLongidsintersect.stream().map(Long::valueOf).collect(Collectors.toList());ListUserDTOusersuserService.listByIds(ids).stream().map(user-BeanUtil.copyProperties(user,UserDTO.class)).collect(Collectors.toList());returnResult.ok(users);}核心是这一行SetStringintersectstringRedisTemplate.opsForSet().intersect(key,key2);它对应 Redis 命令SINTER follows:5 follows:8得到共同关注的用户 id 后再回 MySQL 查询用户信息并转成UserDTO返回给前端。9. 数据链路总结关注链路用户点击关注 - PUT /follow/{id}/true - 获取当前登录用户 userId - tb_follow 新增关系 - Redis: SADD follows:{userId} {followUserId}取关链路用户点击取消关注 - PUT /follow/{id}/false - 获取当前登录用户 userId - tb_follow 删除关系 - Redis: SREM follows:{userId} {followUserId}共同关注链路进入博主主页 - GET /follow/common/{id} - 取 follows:{当前用户id} - 取 follows:{博主id} - Redis 求交集 - 根据交集 id 查用户信息 - 返回共同关注用户列表10. 容易踩的坑1. userId 和 followUserId 容易搞反userId 当前登录用户发起关注的人 followUserId 被关注的人博主2./follow/or/not/{id}不是取消关注接口它实际是判断是否关注接口。3. Redis Set 不是真实数据源真实关注关系仍然在 MySQL 的tb_follow表。Redis Set 是为了共同关注快速求交集。4. 共同关注查的是「两个人关注的人」的交集不是粉丝交集也不是谁关注了我。11. 面试怎么说如果面试官问共同关注怎么实现可以回答关注关系本身保存在 MySQL 的tb_follow表中字段user_id表示发起关注的人follow_user_id表示被关注的人。为了快速查询共同关注我在用户关注或取关时同步维护 Redis Setkey 是follows:{userId}value 是该用户关注的用户 id。查询共同关注时对当前用户和目标用户的两个 Set 做交集SINTER得到共同关注的用户 id再回数据库查询用户信息返回。12. 总结第 9 章前半部分可以浓缩成一句话MySQL 负责保存真实关注关系Redis Set 负责把每个用户关注的人组织成集合从而用交集快速实现共同关注。