Redis实现用户点赞、点赞top5用户排序

案例需求

 

方案分析

 

一个用户对于一个blog只能点赞一次,如果只使用数据库完成该功能,那么用户每点赞一次都要查询、修改数据库,这样无疑增加了数据库的压力,那么引入redis,就可以使用redis的Sorted_Set集合解决该问题(中间件的引入当然是越少越好的,这样有利于系统的稳定性,那此处为了场景演示就引入redis了,毕竟现在有点规模的项目基本都离不开redis)

redis的Sorted_Set集合集合中的元素具有唯一性、有序性,利用唯一性完成一个用户对于一个blog只能点赞一次,利用有序性完成点赞top5的用户排序,key是blog的id,value是用户id和用于排序的时间戳,如果该key的value中有当前登录用户的userId,那么说明该用户对该blog已点赞,那么可以利用该用户的时间戳进行排序,如果没有当前登陆用户的userId,则未点赞。

点赞功能代码实现

   @Override
    public Result   likeBlog(Long id) {
        //获取当前用户
        Long loginUserId = UserHolder.getUser().getId();
        //判断该用户是否点赞
        String key="blog:liked:"+id;

        Double score = stringRedisTemplate.opsForZSet().score(key, loginUserId.toString());
        //score为空说明该用户未点赞
        if (score==null){
            //如果未点赞则点赞成功,数据库点赞数+1,用户对该blog的点赞信息添加到redis的set
            boolean isUpdate = update().setSql("liked = liked + 1").eq("id", id).update();
            if (isUpdate){
                //opsForZSet使用的是scoreSet集合,该集合较于set集合,可以对元素进行排序,基于该特性可以对点赞用户的顺序进行排名
                stringRedisTemplate.opsForZSet().add(key,loginUserId.toString(),System.currentTimeMillis());
            }
        }else {
            //如果已点赞则取消点赞,数据库点赞数-1,用户对该blog的点赞信息从redis的set移除
            boolean isUpdate = update().setSql("liked = liked - 1").eq("id", id).update();
            if (isUpdate){
                stringRedisTemplate.opsForZSet().remove(key,loginUserId.toString());
            }
        }

        return Result.ok();
    }

 最早点赞top5功能代码实现

  @Override
    public Result queryBlogLikes(Long id) {
        //从redis查询top5的点赞用户
        Set<String> top5 = stringRedisTemplate.opsForZSet().range("blog:liked:" + id, 0, 4);
        if (top5==null||top5.isEmpty())
        {
            return Result.ok(Collections.emptyList());
        }
        //从查询的数据中得到top5的用户id
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        String idsStr = StrUtil.join(",", ids);
        //根据id查询top5的用户并返回
//        List<User> users = userService.listByIds(ids);
        //详见黑马点评 实战篇 P10
        List<User> users = userService.query().in("id", ids).last("ORDER BY FIELD (id," + idsStr + ")").list();
        List<UserDTO> userDTOS = users.stream()
                        .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());

        return Result.ok(userDTOS);
    }

细节注意

在最初使用userService.listByIds(ids)查询数据库用户时,查询条件中的id顺序和查询结果的顺序是相反的,具体效果如下:

先查id=5,再查id=1,结果是id=1在前面,id=5在后面,这样导致最早点赞top5的用户顺序相反

为解决上述问题,使用order by对id排序

 注意这里演示的id是确定的数值,在实际代码中要把查询到的用户id列表转换成动态字符串,如下:

//从查询的数据中得到top5的用户id
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        String idsStr = StrUtil.join(",", ids);

 List<User> users = userService.query().in("id", ids).last("ORDER BY FIELD (id," + idsStr + ")").list();
        List<UserDTO> userDTOS = users.stream()
                        .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());

Bug修正

redis报错

WRONGTYPE Operation against a key holding the wrong kind of value

 原因是由于之前点赞功能是使用set集合,使得redis中已经存在了一个名为"blog:liked:23"的key值,添加点赞top5功能后由于把set集合换成了sorted_set集合导致key冲突,解决方案是删掉之前的key值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值