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


Posted in Redis onJune 28, 2022

提到点赞,大家一想到的是不是就是朋友圈的点赞呀?其实点赞对我们来说并不陌生,我们经常会在手机软件或者网页中看到它,今天就让我们来了解一下它的实现吧。我们常见的设计思路大概分为两种:一种自然是用 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 存储结构如图

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

利用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 相关文章推荐
详解缓存穿透击穿雪崩解决方案
May 28 Redis
聊一聊Redis与MySQL双写一致性如何保证
Jun 26 Redis
Redis做数据持久化的解决方案及底层原理
Jul 15 Redis
Redis Cluster 集群搭建你会吗
Aug 04 Redis
在项目中使用redis做缓存的一些思路
Sep 14 Redis
使用redis生成唯一编号及原理示例详解
Sep 15 Redis
Window server中安装Redis的超详细教程
Nov 17 Redis
Redis 异步机制
May 15 Redis
Redis基本数据类型哈希Hash常用操作命令
Jun 01 Redis
浅谈Redis缓冲区机制
Jun 05 Redis
Redis+AOP+自定义注解实现限流
Jun 28 Redis
一文教你快速生成MySQL数据库关系图
Jun 28 #Redis
Redis实现主从复制方式(Master&Slave)
Jun 21 #Redis
浅谈Redis变慢的原因及排查方法
使用Redis实现分布式锁的方法
Jun 16 #Redis
关于Redis的主从复制及哨兵问题
Jun 16 #Redis
Redis实现分布式锁的五种方法详解
Redis实现短信验证码登录的示例代码
Jun 14 #Redis
You might like
实例讲解php将字符串输出到HTML
2019/01/27 PHP
jquery 输入框数字限制插件
2009/11/10 Javascript
在一个js文件里远程调用jquery.js会在ie8下的一个奇怪问题
2010/11/28 Javascript
当自定义数据属性为json格式字符串时jQuery的data api问题探讨
2013/02/18 Javascript
jQuery JSON实现无刷新三级联动实例探讨
2013/05/28 Javascript
JS往数组中添加项性能分析
2015/02/25 Javascript
JavaScript中rem布局在react中的应用
2015/12/09 Javascript
JavaScript Math.round() 方法
2015/12/18 Javascript
基于jQuery实现选取月份插件附源码下载
2015/12/28 Javascript
理解Javascript图片预加载
2016/02/23 Javascript
解决微信二次分享不显示摘要和图片的问题
2017/08/18 Javascript
简单说说angular.json文件的使用
2018/10/29 Javascript
微信小程序中的canvas 文字断行和省略号显示功能的处理方法
2018/11/14 Javascript
如何优雅地在Node应用中进行错误异常处理
2019/11/25 Javascript
jQuery实现简单飞机大战
2020/07/05 jQuery
[42:50]NB vs VP 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
python操作数据库之sqlite3打开数据库、删除、修改示例
2014/03/13 Python
Python首次安装后运行报错(0xc000007b)的解决方法
2016/10/18 Python
Django跨域请求问题的解决方法示例
2018/06/16 Python
python 通过SSHTunnelForwarder隧道连接redis的方法
2019/02/19 Python
python2.7 安装pip的方法步骤(管用)
2019/05/05 Python
python3 selenium自动化测试 强大的CSS定位方法
2019/08/23 Python
安装PyInstaller失败问题解决
2019/12/14 Python
Python龙贝格法求积分实例
2020/02/29 Python
python实现录音功能(可随时停止录音)
2020/10/26 Python
利于python脚本编写可视化nmap和masscan的方法
2020/12/29 Python
英国最大的运动营养公司之一:LA Muscle
2018/07/02 全球购物
SmartBuyGlasses意大利:购买太阳镜、眼镜和隐形眼镜
2018/11/20 全球购物
北欧最好的童装网上商店:Babyshop
2019/09/15 全球购物
分解成质因数(如435234=251*17*17*3*2,据说是华为笔试题)
2014/07/16 面试题
省三好学生申请材料
2014/01/22 职场文书
公司保密承诺书
2014/03/27 职场文书
保研推荐信
2014/05/09 职场文书
2015秋季开学演讲稿范文
2015/07/16 职场文书
2015年教师节感言
2015/08/03 职场文书
教师网络培训心得体会
2016/01/09 职场文书