利用Redis实现点赞功能的示例代码

 更新时间:2022年06月28日 15:25:30   作者:阿Q说代码  
点赞对我们来说并不陌生,我们经常会在手机软件或者网页中看到它。今天就让我们来了解一下如何用Redis实现这一功能吧,感兴趣的可以尝试一下

提到点赞,大家一想到的是不是就是朋友圈的点赞呀?其实点赞对我们来说并不陌生,我们经常会在手机软件或者网页中看到它,今天就让我们来了解一下它的实现吧。我们常见的设计思路大概分为两种:一种自然是用 MySQL 等数据库直接落地存储, 另外一种就是将点赞的数据保存到 Redis 等缓存里,在一定时间后刷回 MySQL 等数据库。

MySQL 和 Redis优缺点

首先我们来说一下两种方法各自的优缺点:我们以 MySQL 和 Redis 为例。

1、直接写入数据库:

优点:这种方法实现简单,只需完成数据库的增删改查就行;

缺点:数据库读写压力大,如果遇到热门文章在短时间内被大量点赞的情况,直接操作数据库会给数据库带来巨大压力,影响效率。

2、使用 Redis 缓存:

优点:性能高,读写速度快,缓解数据库读写的压力;

缺点:开发复杂,不能保证数据安全性即 redis 挂掉的时候会丢失数据, 同时不及时同步 redis 中的数据, 可能会在 redis 内存置换的时候被淘汰掉。不过对于点赞数据我们不需要那么精确,丢失一点数据问题不大。

接下来就从以下三个方面对点赞功能做详细的介绍

•Redis 缓存设计

•数据库设计

•开启定时任务持久化存储到数据库

1、Redis 缓存设计及实现

Redis 的整合我们在上一篇文章中已经介绍过了,此处就不再赘述了。我们了解到,我们在做点赞的时候需要记录以下几类数据:一类是某用户被其他用户点赞的详细记录,一类是。考虑到查询与存取方便快捷,我这边采用 Hash 结构进行存储,存储结构如下:

(1)某用户被其他用户点赞的详细记录: MAP_USER_LIKED 为键值, 被点赞用户id::点赞用户id 为 filed, 1或者0 为 value

(2)某用户被点赞的数量统计: MAP_USER_LIKED_COUNT 为键值, 被点赞用户id 为 filed, count 为 value

部分代码如下

/**
* 将用户被其他用户点赞的数据存到redis
*/
@Override
public void saveLiked2Redis(String likedUserId, String likedPostId) {
    String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
    redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED,key, LikedStatusEnum.LIKE.getCode());
}

//取消点赞
@Override
public void unlikeFromRedis(String likedUserId, String likedPostId) {
    String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
    redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED,key,LikedStatusEnum.UNLIKE.getCode());
}

/**
* 将被点赞用户的数量+1
*/
@Override
public void incrementLikedCount(String likedUserId) {
    redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,likedUserId,1);
}

//-1
@Override
public void decrementLikedCount(String likedUserId) {
    redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, -1);
}

/**
* 获取Redis中的用户点赞详情记录
*/
@Override
public List<UserLikeDetail> getLikedDataFromRedis() {
    Cursor<Map.Entry<Object,Object>> scan = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE);
    List<UserLikeDetail> list = new ArrayList<>();
    while (scan.hasNext()){
        Map.Entry<Object, Object> entry = scan.next();
        String key = (String) entry.getKey();
        String[] split = key.split("::");
        String likedUserId = split[0];
        String likedPostId = split[1];
        Integer value = (Integer) entry.getValue();
        //组装成 UserLike 对象
        UserLikeDetail userLikeDetail = new UserLikeDetail(likedUserId, likedPostId, value);
        list.add(userLikeDetail);
        //存到 list 后从 Redis 中删除
        redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
    }
    return list;
}

/**
* 获取Redis中的用户被点赞数量
*/
@Override
public List<UserLikCountDTO> getLikedCountFromRedis() {
    Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE);
    List<UserLikCountDTO> list = new ArrayList<>();
    while(cursor.hasNext()){
        Map.Entry<Object, Object> map = cursor.next();
        String key = (String) map.getKey();
        Integer value = (Integer) map.getValue();
        UserLikCountDTO userLikCountDTO = new UserLikCountDTO(key,value);
        list.add(userLikCountDTO);
        //存到 list 后从 Redis 中删除
        redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,key);
    }
    return list;
}

Redis 存储结构如图

2、数据库设计

这里我们可以和直接将点赞数据存到数据库一样,设计两张表:

(1)用户被其他用户点赞的详细记录:user_like_detail

DROP TABLE IF EXISTS `user_like_detail`;
CREATE TABLE `user_like_detail`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `liked_user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '被点赞的用户id',
  `liked_post_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '点赞的用户id',
  `status` tinyint(1) NULL DEFAULT 1 COMMENT '点赞状态,0取消,1点赞',
  `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `liked_user_id`(`liked_user_id`) USING BTREE,
  INDEX `liked_post_id`(`liked_post_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户点赞表' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

(2)用户被点赞的数量统计:user_like_count

DROP TABLE IF EXISTS `user_like_count`;
CREATE TABLE `user_like_count`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `like_num` int(11) NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

3、开启定时任务持久化存储到数据库

我们使用 Quartz 来实现定时任务,将 Redis 中的数据存储到数据库中,为了演示效果,我们可以设置一分钟或者两分钟存储一遍数据,这个视具体业务而定。在同步数据的过程中,我们首先要将 Redis 中的数据在数据库中进行查重,舍弃重复数据,这样我们的数据才会更加准确。

部分代码如下

//同步redis的用户点赞数据到数据库
@Override
@Transactional
public void transLikedFromRedis2DB() {
    List<UserLikeDetail> list = redisService.getLikedDataFromRedis();
    list.stream().forEach(item->{
        //查重
        UserLikeDetail userLikeDetail = userLikeDetailMapper.selectOne(new LambdaQueryWrapper<UserLikeDetail>()
           .eq(UserLikeDetail::getLikedUserId, item.getLikedUserId())
           .eq(UserLikeDetail::getLikedPostId, item.getLikedPostId()));
        if (userLikeDetail == null){
            userLikeDetail = new UserLikeDetail();
            BeanUtils.copyProperties(item, userLikeDetail);
            //没有记录,直接存入
            userLikeDetail.setCreateTime(LocalDateTime.now());
            userLikeDetailMapper.insert(userLikeDetail);
        }else{
            //有记录,需要更新
            userLikeDetail.setStatus(item.getStatus());
            userLikeDetail.setUpdateTime(LocalDateTime.now());
            userLikeDetailMapper.updateById(item);
        }
    });
}

@Override
@Transactional
public void transLikedCountFromRedis2DB() {
    List<UserLikCountDTO> list = redisService.getLikedCountFromRedis();
    list.stream().forEach(item->{
        UserLikeCount user = userLikeCountMapper.selectById(item.getKey());
        //点赞数量属于无关紧要的操作,出错无需抛异常
        if (user != null){
            Integer likeNum = user.getLikeNum() + item.getValue();
            user.setLikeNum(likeNum);
            //更新点赞数量
            userLikeCountMapper.updateById(user);
        }
    });
}

至此我们就实现了基于 Redis 的点赞功能,我们还需要注意一点:查询用户点赞情况时,需要同时查询数据库+缓存中的数据。

以上就是利用Redis实现点赞功能的示例代码的详细内容,更多关于Redis点赞功能的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Redis缓存与Mysql如何保证双写一致

    详解Redis缓存与Mysql如何保证双写一致

    缓存和数据库如何保证数据的一致是个很经典的问题,这篇文章就来和大家一起探讨一下Redis缓存与Mysql如何保证双写一致,感兴趣的小伙伴可以参考下
    2023-12-12
  • Redis如何实现分布式锁

    Redis如何实现分布式锁

    这篇文章主要介绍了Redis如何实现分布式锁问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • Windows下搭建Redis哨兵集群模式的方法步骤

    Windows下搭建Redis哨兵集群模式的方法步骤

    哨兵模式,是基于主从复制模式,主从复制的优点全都拥有,并且主从可以实现自动切换,故障转移等功能,本文主要介绍了Windows下搭建Redis哨兵集群模式的方法步骤,文中通过图文介绍的非常详细,感兴趣的小伙伴们可以参考一下,需要的朋友可以参考下
    2023-09-09
  • Redis的setNX分布式锁超时时间失效 -1问题及解决

    Redis的setNX分布式锁超时时间失效 -1问题及解决

    这篇文章主要介绍了Redis的setNX分布式锁超时时间失效 -1问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • Redis实现排名功能的示例代码

    Redis实现排名功能的示例代码

    本文主要介绍了Redis实现排名功能的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Redis主从集群切换数据丢失的解决方案

    Redis主从集群切换数据丢失的解决方案

    这篇文章主要介绍了Redis主从集群切换数据丢失的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 浅谈Redis安全策略

    浅谈Redis安全策略

    Redis 提供了诸多安全策略,比如为了保证数据安全,提供了设置密码的功能。本文就详细的介绍了一下Redis安全策略,具有一定的参考价值,感兴趣的可以了解一下
    2021-11-11
  • Redis批量生成数据的实现

    Redis批量生成数据的实现

    本文主要介绍了Redis批量生成数据的实现,主要介绍了两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Redis字符串类型的常用命令小结

    Redis字符串类型的常用命令小结

    这篇文章给大家整理了在操作Redis字符串类型中的常用命令,文章总结的很全面,对大家学习Redis具有一定的参考借鉴价值,下面来一起看看吧。
    2016-09-09
  • redis保存session信息的示例代码

    redis保存session信息的示例代码

    本文实现一个将session信息保存在 redis中,多个tomcat中的工程都从redis获取session信息的示例,本文给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-01-01

最新评论