redis实现点赞功能,数据同步

1 篇文章 0 订阅

本文使用hash类型存储点赞记录及统计总数,点赞记录使用博客id+用户id作为key,存储value值为点赞或者取消点赞状态。

点赞或取消点赞

点赞或取消点赞的方法,不会删除redis的记录,只会去更新对应的点赞状态。点赞或取消点赞需要对点赞总数进行数量+1或者-1的操作,这个适合需要去判断总量key是否在缓存中存在,如果没有需要去数据库获取再刷进缓存里面。

    @Override
    public String likeOrNot(Long blogId, Long userId, Integer likeDelFlag) {
        redisTemplate.opsForHash().put(RedisPrefixConstant.BLOG_LIKE_USER_PREFIX, RedisPrefixConstant.BLOG_LIKE_USER_PREFIX + RedisPrefixConstant.COLON + blogId + RedisPrefixConstant.COLON + userId, likeDelFlag);

        String key = RedisPrefixConstant.BLOG_LIKE_COUNT_PREFIX + RedisPrefixConstant.COLON + blogId;

        String value = UUID.randomUUID().toString().replaceAll("-", "");

        //对总量操作
        return this.addOrDelCount(RedisPrefixConstant.BLOG_LIKE_COUNT_PREFIX, key, likeDelFlag, value, blogId, 0);
    }
    
    /**
     * 增加或删除统计数量
     *
     * @param likeDelFlag
     * @param prefixKey
     * @param key
     */
    private void addOrDelCount(String prefixKey, String key, Integer likeDelFlag) {
        if (DelFlagConstant.DEL_FLAG__EXIST.equals(likeDelFlag)) {
            //统计数加1
            redisTemplate.opsForHash().increment(prefixKey, key, 1);
        } else {
            //统计数减1
            redisTemplate.opsForHash().increment(prefixKey, key, -1);
        }
    }

    /**
     * 统计数量操作
     *
     * @param likeDelFlag 删除标记
     * @param prefixKey   开始的key
     * @param key         统计key
     * @param lockValue   锁的值
     * @param blogId      博客id
     * @param retryCount  重试次数
     */
    private String addOrDelCount(String prefixKey, String key, Integer likeDelFlag, String lockValue, Long blogId, int retryCount) {
        //判断是否存在统计数缓存
        Boolean hasKey = redisTemplate.opsForHash().hasKey(prefixKey, key);
        if (hasKey) {
            //存在直接操作
            this.addOrDelCount(prefixKey, key, likeDelFlag);
            return "操作成功";
        } else {
            String lockPrefix = RedisPrefixConstant.BLOG_LIKE_COUNT_LOCK_PREFIX;
            try {

                Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockPrefix, lockValue, 1L, TimeUnit.SECONDS);
                // 加锁失败
                if (!flag) {
                    Thread.sleep(50);
                    if (retryCount < 3) {
                        //三次重试
                        retryCount++;
                        this.addOrDelCount(prefixKey, key, likeDelFlag, lockValue, blogId, retryCount);
                    } else {
                        return "操作失败";
                    }

                } else {
                    //去数据库查询数量
                    Blog blog = baseMapper.selectById(blogId);
                    Integer likeCount = 0;

                    if (null != blog) {
                        if (null != blog.getLikeCount()) {
                            likeCount = blog.getLikeCount();
                        }
                    }
                    //将统计量写入缓存
                    redisTemplate.opsForHash().put(prefixKey, key, likeCount);
                    //对统计量+1/-1
                    this.addOrDelCount(prefixKey, key, likeDelFlag);
                    return "操作成功";
                }
            } catch (Exception e) {
                log.error(e.getMessage());
                return "操作失败";
            } finally {
                if (lockValue.equals(redisTemplate.opsForValue().get(lockPrefix))) {
                    redisTemplate.delete(RedisPrefixConstant.BLOG_LIKE_COUNT_LOCK_PREFIX);
                }
            }
        }
        return "操作失败";
    }

对点赞记录写入到数据库

通过hash里面的scan方法获取到所有的map对象,然后遍历获取里面的属性,转换为对应的实体类,调用插入或者更新方法读取入库。对于点赞记录,如果数据库没有记录会插入点赞记录,后续如果取消或者取消再点赞这些操作,只会在数据库存储一条记录,然后更新对应的字段状态。

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRedisToMysqlOfLikeCount() {
        Cursor<Map.Entry<Object, Object>> scan = redisTemplate.opsForHash().scan(RedisPrefixConstant.BLOG_LIKE_COUNT_PREFIX, ScanOptions.NONE);
        while (scan.hasNext()) {
            Map.Entry<Object, Object> next = scan.next();
            String key = (String) next.getKey();
            //去掉前缀
            key = key.replace(RedisPrefixConstant.BLOG_LIKE_COUNT_PREFIX + RedisPrefixConstant.COLON, "");

            Long blogId = Long.parseLong(key);
            Integer likeCount = (Integer) next.getValue();

            //替换属性更新
            LambdaUpdateWrapper<Blog> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.eq(Blog::getId, blogId);
            updateWrapper.set(Blog::getLikeCount, likeCount);
            this.update(updateWrapper);
        }
        //删除缓存
        redisTemplate.delete(RedisPrefixConstant.BLOG_LIKE_COUNT_PREFIX);
    }

对点赞总数量写入到数据库

操作如上,点赞数量也可以使用string类型来存储,代码差不多,就是获取所有的数据时,需要先去获取所有的key再用key去获取所有的value。

@Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRedisToMysqlOfLike() {
        List<BlogLike> likes = new ArrayList<>();
        Cursor<Map.Entry<Object, Object>> scan = redisTemplate.opsForHash().scan(RedisPrefixConstant.BLOG_LIKE_USER_PREFIX, ScanOptions.NONE);
        while (scan.hasNext()) {
            Map.Entry<Object, Object> next = scan.next();
            String key = (String) next.getKey();
            //去掉前缀
            key = key.replace(RedisPrefixConstant.BLOG_LIKE_USER_PREFIX + RedisPrefixConstant.COLON, "");
            //分离出 likedUserId,giveLikeId
            String[] split = key.split(RedisPrefixConstant.COLON);
            Long blogId = Long.parseLong(split[0]);
            Long userId = Long.parseLong(split[1]);
            Integer delFlag = (Integer) next.getValue();

            //去查询有没有该数据 如果有则替换属性更新
            LambdaQueryWrapper<BlogLike> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(BlogLike::getBlogId, blogId);
            queryWrapper.eq(BlogLike::getUserId, userId);
            BlogLike blogLike = baseMapper.selectOne(queryWrapper);
            if (null == blogLike) {
                //组装成对象
                blogLike = new BlogLike(blogId, userId, delFlag);
            } else {
                blogLike.setDelFlag(delFlag);
            }
            likes.add(blogLike);
        }
        this.saveOrUpdateBatch(likes);
        //删除缓存
        redisTemplate.delete(RedisPrefixConstant.BLOG_LIKE_USER_PREFIX);
    }

定时任务

最后使用定时调度方法去调用上面两个方法,将数据同步到数据库即可,可以使用xxl-job或者直接使用@Scheduled注解即可,对于点赞记录,可以设置更新频繁一点,比如两个小时一次,对于点赞总量,可以设置成一天更新一次。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值