ZSET
问题:防重复点赞,通过 Redis 判断用户是否已点赞,避免重复操作。
数据库和 Redis 协同维护点赞状态,确保前端展示一致性。
利用 Redis 的原子操作(如 ZSET)快速处理点赞 / 取消点赞请求,减少数据库压力。
基于 Redis 有序集合按时间戳排序,高效获取点赞排行榜。
Redis数据结构:Redis 有序集合(ZSET),存储用户 ID 和点赞时间戳,保证唯一性(ZADD
天然去重);通过 ZSCORE
快速判断用户是否已点赞;使用 ZRANGE
获取点赞排行榜(如前 5 名);
Redis命令:ZADD key score member(存储用户点赞记录)、ZSCORE key member(检查用户是否已点赞)、ZREM key member(取消点赞时移除用户记录)
-
@Override public Result likeBlog(Long id) { // 判断当前用户是否点赞 UserDTO user = UserHolder.getUser(); if(user == null){ return Result.fail("用户未登录"); } Long userId = user.getId(); String key = "blog:liked:" + id; Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString()); if(score == null){ //如果未点赞则点赞 //更新点赞数量到数据库 boolean issuccess = update().setSql("liked = liked + 1").eq("id", id).update(); //保存点赞用户到redis集合, 按时间排序最先点赞的会排在前面 if(issuccess){ stringRedisTemplate.opsForZSet().add(key,userId.toString(),System.currentTimeMillis()); } } else { //如果已点赞则取消点赞 //数据库减去 boolean issuccess = update().setSql("liked = liked - 1").eq("id", id).update(); //redis中删除 if(issuccess){ stringRedisTemplate.opsForZSet().remove(key,userId.toString()); } } return Result.ok(); } // 点赞排行 @Override public Result queryBlogLikes(Long id) { String key = BLOG_LIKED_KEY + id; // 1.查询top5的点赞用户 zrange key 0 4 Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4); if (top5 == null || top5.isEmpty()) { return Result.ok(Collections.emptyList()); } // 2.解析出其中的用户id List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList()); String idStr = StrUtil.join(",", ids); // 3.根据用户id查询用户 WHERE id IN ( 5 , 1 ) ORDER BY FIELD(id, 5, 1) 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()); // 4.返回 return Result.ok(userDTOS); }