前言
这是一个用redis加定时任务实现的点赞功能
一、项目场景
首先的话,我们可以看到这是一个分页的评论查询,查询结果有用户头像,评论点赞数,是否点赞,用户名等信息。我们要做的就是点赞功能的快速实现。我认为最好的方法就是用Redis,因为Redis是存放在内存当中的,比我们直接操作数据库,存入磁盘来得要快。
二、实现思路步骤
1.先来看看我们往前端传递的参数
代码如下(示例):
@Data
public class CommentVo implements Serializable {
private Long id;
/**
* 用户ID(外键,引用 user 表的 id,这里是当前登录用户的id)
*/
**private Long userid;
/**
* 题目ID(这个是用来查询不同题目不同id的)
*/**
private Long questionid;
/**
* 用户关联表(这里应该是发表评论人的用户)
*/
private UserVO userVO;
/**
* 评论内容
*/
private String commentcontent;
/**
* 更新时间
*/
private Date updatetime;
/**
* 喜欢数量
*/
private Integer likescount;
/**
* 收藏数量
*/
private Integer collectscount;
/**
* 是否喜欢
* */
private Integer isLike;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
2.实现思路
现在我们知道了,用户查询评论时候存在的各个参数,那我可以开始思考怎么用redis来进行存放
个人思路
1.将喜欢的数量和评论的id进行存放以hash方式存入redis中
2.当用户点击喜欢或者取消喜欢的时候,我们只需要根据评论的id将它取出,进行自增或者自减就好了
Integer o = (Integer) redisTemplate.opsForHash().get(key + commentLikeDto.getCommentId(), commentLikeDto.getCommentId().toString());
log.info("当前点赞数"+o);
o++;
redisTemplate.opsForHash().put(key + commentLikeDto.getCommentId(), commentLikeDto.getCommentId().toString(), o);
scheduledCommmet.addcomment(commentLikeDto.getCommentId(),o);
Integer a = (Integer) redisTemplate.opsForHash().get(key + commentLikeDto.getCommentId(), commentLikeDto.getCommentId().toString());
log.info("当前点赞数"+a);
其实这里我们可以自己用redis自带的自增功能函数
// 假设key是哈希结构的根键,commentId是具体的字段键
String hashKey = key + commentLikeDto.getCommentId();
String field = commentLikeDto.getCommentId().toString();
// 使用increment方法进行自增操作,这里假设点赞数初始值为0或已存在的值
Long incrementedValue = redisTemplate.opsForHash().increment(hashKey, field, 1);
// incrementedValue现在包含了自增后的值
log.info("当前点赞数: " + incrementedValue);
// 将自增后的点赞数同步到其他地方,如果需要的话
scheduledCommmet.addcomment(commentLikeDto.getCommentId(), incrementedValue.intValue());
// 如果需要再次确认Redis中的值(通常是不必要的,因为increment操作是原子的)
Long confirmedValue = (Long) redisTemplate.opsForHash().get(hashKey, field);
log.info("Redis中确认的当前点赞数: " + confirmedValue);
但是这样仅仅只是将当前评论的喜欢数量进行了加减,我们要做的是,要求喜欢只能点赞为一次,也就是前端要让那个心亮起来,也就是说我们还需要将是否喜欢也存放在redis中
3.那么我们就将当前的用户id和评论id存放进去
- 评论id为key,用户id为val,假如我是用户点赞后,我们将这些信息存放进去,然后取消点赞就将,我们的userid删掉这样即可
redisTemplate.opsForHash().put("commentLike",commentLikeDto.getCommentId().toString(),commentLikeDto.getUserId().toString());
redisTemplate.opsForHash().delete("commentLike",commentLikeDto.getCommentId().toString(),commentLikeDto.getUserId().toString());
- 但是我们还是我有将我们的数据放入到数据库当中,这样可能造成我们的数据不一致,并没更新的数据,所以接下来我们要做一个定时任务将点赞的一系列的信息存放到数据库中
4.定时任务更新到数据库当中
代码示例–>
@Component
@Slf4j
public class ScheduledCommmet {
@Resource
CommentLikeService commentLikeService;
@Resource
UserCommentsService userCommentsService;
static Map<Long, Integer> commentMap = new HashMap<>();//这个是存放到user——comment表中的评论记录着喜欢的数量
static Map<Long, Long> commentLikeMap = new HashMap<>();//用户喜欢的评论
@Scheduled(fixedRate = 10000) // 每10秒执行一次
public void doSomething() {
//todo 定时存放到数据库
commentMap.forEach((commentId,likeCount)->{
LambdaQueryWrapper<UserComments> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(UserComments::getId,commentId);
if (userCommentsService.getOne(lambdaQueryWrapper)!=null){
UserComments userComments = userCommentsService.getOne(lambdaQueryWrapper);
userComments.setLikescount(likeCount);
userCommentsService.updateById(userComments);
}else {
UserComments userComments = new UserComments();
userComments.setId(commentId);
userComments.setLikescount(likeCount);
userCommentsService.save(userComments);
}
});
log.info("定时存放到数据库{}" ,commentMap.toString());
commentMap.clear();
commentLikeMap.forEach((commentId,userId)->{
LambdaQueryWrapper<CommentLike> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(CommentLike::getUserId,userId);
lambdaQueryWrapper.eq(CommentLike::getCommentId,commentId);
if (commentLikeService.getOne(lambdaQueryWrapper)!=null){
CommentLike commentLike = commentLikeService.getOne(lambdaQueryWrapper);
commentLike.setIsLike(1);
commentLikeService.updateById(commentLike);
}else {
CommentLike commentLike = new CommentLike();
commentLike.setUserId(userId);
commentLike.setCommentId(commentId);
commentLike.setIsLike(1);
commentLikeService.save(commentLike);
}
});
log.info("定时存放到数据库",commentLikeMap.toString());
commentLikeMap.clear();
}
public void addCommentLike(Long commentId, Long userId) {
if (commentLikeMap.containsKey(commentId)){
commentLikeMap.remove(commentId,userId);
}
commentLikeMap.put(commentId,userId);
}
public void addcomment( Long commentId,Integer likeCount) {
if (commentMap.containsKey(commentId)){
commentMap.replace(commentId,likeCount);
}
commentMap.put(commentId,likeCount);
}
}
- 定义了2个Map集合,用来接收我们的数据,数据结构如上
- 用@Scheduled定义一个定时任务,10秒存放一次,虽然当别人用户查看当前评论时可能会存在数据不一致,但是因为是点赞场景,所以我认为没必要保持每时每刻都保证数据的一致,只要让当前的点赞前后的变化,能让当前用户得到及时的反馈即可
- 当然我们在存放数据的时候,我们可以给redis设置一个过期时间,这个过期时间必须大于定时存放的时间
总结
如果我们使用数据库,当用户频繁点赞和取消点赞的时候,就要不断地操作数据库,速度太慢,我们不如将点赞的数据存放在redis中,让用户的体验更好!!