Redis - 顺序队列 SortedSet

SortedSet

  1. 定义:Redis 中的有序集合
    1. key:集合的名字 (此处业务逻辑中是 Blog 对象名)
    2. field:集合成员对象 (此处业务逻辑中使用 SortedSet,其中 score 为点赞时间 )
    3. score:集合成员对象用于排序的依据 (此处业务逻辑中使用点赞时间 System.currentTimeMillis() 作为排序依据)
  2. 功能
    1. 元素唯一:存储点赞当前 Blog 的用户集合,避免重复点赞
    2. 根据 score 排序:存储点赞时间,需要时可以按照点赞时间顺序进行排序
    3. 根据 field 查找:可以判断当前登录用户是否已经点赞当前 Blog
  3. 常用命令
    1. 添加:ZADD key score1 elem1 score2 elem2 …
    2. 单个查询:ZSCORE key myElem
      1. 注:实际上是在查询 field 的 score 值,如果 score 为 null 则说明不存在 myElem 元素
    3. 范围查询:ZRANGE key myLowRank myHighRank

示例:点赞排行榜

功能点

  1. 当前登录用户对 Blog 进行点赞
  2. 当前登录用户对 Blog 取消点赞
  3. 判断当前用户是否已经对 Blog 点赞
  4. 查询最早点赞当前 Blog 的 5 个用户

业务逻辑

业务方案

数据结构:SortedSet

增加表的字段:@TableField(exist = false)

  1. 存储位置:逻辑属于 MySQL 数据库的字段,实际上字段并不存在
  2. 定义:MySQL 中的一个虚拟字段,可以给其赋值但是不会真正写入数据库
  3. 功能:判断当前登录用户是否已经给当前 Blog 点赞
  4. 实现逻辑
    1. 在 Redis 中查找已经点赞当前 Blog 的用户集合 Set
    2. 判断 Set 中是否存在当前登录用户
    3. 如果存在,则 Blog 的 isLike 属性修改为 true
    4. 如果不存在,则 Blog 的 isLike 属性修改为 false

代码实现

  1. BlogController

    @GetMapping("/likes/{id}")
    public Result queryBlogLikes(@PathVariable("id") Long blogId) {
    		return blogService.queryBlogLikes(blogId);
    
  2. BlogService

    public Result queryBlogLikes(Long blogId) {
    		String key = BLOG_LIKED_KEY + blogId;
    		// 查询最早点赞的 5 个用户的 id (String 类型)
    		Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
    		if(top5 == null || top5.isEmpty())
    				return Result.ok(Collections.emptyList());
    		// 获取 5 个用户的 Long 类型 id 并转为 List 数组
    		List<Long> userIds = top5.stream().map(Long::valueOf).collect(Collectors.toList());
    		// 查询 5 个用户的对象并转为 DTO 返回
    		String idStr = StrUtil.join(",", userIds);
    		List<UserDTO> userDTOS = userService.query()
    					.in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list()
    					.stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class).collect(Collectors.toList());
    		return Result.ok(userDTOS);
    }
    
  3. BlogServiceImpl

    @Override
    public Result likeBlog(Long blogId) {
    
    		Long userId = UserHolder.getUser().getId();
    		String key = BLOG_LIKED_KEY + blogId;
    		Double score = stringRedisTemplate.opsForZset().score(key, userId.toString());
    		
    		// 当前用户没有点赞 -> 点赞
    		if(score != null) {
    				// 先更新数据库
    				boolean updateSuccess = update().setSql("like = like + 1").eq("id", blogId).update();
    				if(updateSuccess)  {
    						stringRedisTemplate.opsForZset().put(key, userId.toString(), System.currentTimeMillis());
    				}
    				return Result.ok();
    		}
    		// 当前用户已经点赞 -> 取消点赞
    		boolean updateSuccess = update().setSql("liked = liked - 1").eq("id", blogId).update();
    		if(updateSuccess) {
    				stringRedisTemplate.opsForZset().remove(key, userId.toString());
    		}
    		return Result.ok();
    }
    
    private void isBlogLiked(Blog blog) {
    		// 获取登录用户(因为不登录也可以看这个界面,所以需要处理用户未登录的情况)
    		UserDTO user = UserHolder.getUser();
    		if(user = null) return;
    		Long userId = user.getId();
    		
    		String key = BLOG_LIKED_KEY + blog.getId();
    		Double score = stringRedisTemplate.opsForZset().score(key, userId.toString());
    		blog.setIsLike(score != null);
    }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值