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

目录
  • MySQL 和 Redis优缺点
  • 1、Redis 缓存设计及实现
    • 部分代码如下
    • Redis 存储结构如图
  • 2、数据库设计
  • 3、开启定时任务持久化存储到数据库
    • 部分代码如下

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

(0)

相关推荐

  • 点赞功能使用MySQL还是Redis

    目录 1. 新手常犯的错误 2. 使用Iterator的remove()方法 3. 使用for循环正序遍历 4. 使用for循环倒序遍历 5. 使用Iterator的remove()方法 6. 使用for循环正序遍历 7. 使用for循环倒序遍历 这是最近面试时被问到的1道面试题,本篇博客对此问题进行总结分享. 1. 新手常犯的错误 可能很多新手(包括当年的我,哈哈)第一时间想到的写法是下面这样的: public static void main(String[] args) { List<St

  • springboot +redis 实现点赞、浏览、收藏、评论等数量的增减操作

    springboot +redis 实现点赞.浏览.收藏.评论等数量的增减操作 前言 第一次写博客,记录一下: 最近做了一个帖子的收藏.点赞数量的功能,其实之前也做过类似的功能,因为之前一直使用的mysql 总是感觉对于这种频繁需要改变的值,不应该给予Mysql过大的压力,本文章采用的是redis 做了持久化.下面贴出关键代码:DataResponse是项目中使用的结果封装实体类:forumDTO是此功能的参数实体,如果有需要请留言. 常量如下: private static final Str

  • 使用 Redis 缓存实现点赞和取消点赞的示例代码

    点赞功能是很多平台都会提供的一个功能,那么,我们要如何实现点赞和取消点赞呢? 这篇文章总结了我在项目中实现点赞的方法. 缓存 vs 数据库? 首先我们要考虑的是数据要放到哪里,很多时候我们都会把数据放到数据库(如 MySQL),由于关系型数据库的稳定性,大部分场景下我们也都会使用关系数据库来存储数据. 不过,在一些特殊的场景下,传统的关系型数据库很可能无法满足我们的需求.比如,在访问量较大的情况下,数据库很可能会宕机或者访问速度非常慢.这对用户来说是不能容忍的.因此就有了非关系型数据库,如 Re

  • 基于redis实现的点赞功能设计思路详解

    前言 点赞其实是一个很有意思的功能.基本的设计思路有大致两种, 一种自然是用mysql等 数据库直接落地存储, 另外一种就是利用点赞的业务特征来扔到redis(或memcache)中, 然后离线刷回mysql等. 直接写入Mysql 直接写入Mysql是最简单的做法. 做两个表即可, 1.post_like 记录文章被赞的次数,已有多少人赞过这种数据就可以直接从表中查到; 2.user_like_post 记录用户赞过了哪些文章, 当打开文章列表时,显示的有没有赞过的数据就在这里面; 缺点 1.

  • 使用Redis实现点赞取消点赞的详细代码

    前言 异步实现 代码实现: /** * * @param userId 点赞的人 * @param type 点赞与取消点赞的表示 * @param textId 文章ID * @param entityUserId -- 被点赞的人,文章作者 */ private void like(long userId,int type,int textId,long entityUserId){ redisTemplate.execute(new SessionCallback() { @Overrid

  • 手把手教你用Redis 实现点赞功能并且与数据库同步

    目录 一.Redis 缓存设计及实现 SpringBoot整合Redis 二.点赞数据在 Redis 中的存储格式 用 Redis 存储两种数据: 三.数据库设计 四.开启定时任务持久化存储到数据库 1. 添加依赖 2. 编写配置文件 3. 编写执行任务的类继承自 QuartzJobBean 五.注意事项 一.Redis 缓存设计及实现 Linux下安装Redis或者Docker下安装Redis并且启动(redis-server) SpringBoot整合Redis 1.在 pom.xml 中引

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

    目录 MySQL 和 Redis优缺点 1.Redis 缓存设计及实现 部分代码如下 Redis 存储结构如图 2.数据库设计 3.开启定时任务持久化存储到数据库 部分代码如下 提到点赞,大家一想到的是不是就是朋友圈的点赞呀?其实点赞对我们来说并不陌生,我们经常会在手机软件或者网页中看到它,今天就让我们来了解一下它的实现吧.我们常见的设计思路大概分为两种:一种自然是用 MySQL 等数据库直接落地存储, 另外一种就是将点赞的数据保存到 Redis 等缓存里,在一定时间后刷回 MySQL 等数据库

  • Java利用Redis实现消息队列的示例代码

    本文介绍了Java利用Redis实现消息队列的示例代码,分享给大家,具体如下: 应用场景 为什么要用redis? 二进制存储.java序列化传输.IO连接数高.连接频繁 一.序列化 这里编写了一个java序列化的工具,主要是将对象转化为byte数组,和根据byte数组反序列化成java对象; 主要是用到了ByteArrayOutputStream和ByteArrayInputStream; 注意:每个需要序列化的对象都要实现Serializable接口; 其代码如下: package Utils

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

    目录 前言 一.实现思路 二.具体实现 小结 前言 之前在消费金融平台的时候,公司有一个专门给线下销售人员使用的APP,APP记录销售推广公司贷款产品赚取的佣金以及一些门店开拓和打卡的功能,后端是由我和另外一个同事开发的,其中有一个模块是全国门店内的销售佣金实时排名,说到排名很多人的第一反应都是这是个Top N的问题,从数据库取出来用MySQL的top函数不就可以实现了,事实上当时无法从表里取到数据,数据还要配合权限,有全国和大区的排名,还需要计算大区经理下所有人员的有效佣金,还要求是实时的,从

  • Redis实现订单自动过期功能的示例代码

    前言 用户下单后,规定XX分钟后自动设置为"已过期",不能再发起支付.项目类似此类"过期"的需求,笔者提供一种使用Redis的解决思路,结合Redis的订阅.发布和键空间通知机制(Keyspace Notifications)进行实现. 配置redis.confg notify-keyspace-events选项默认是不启用,改为notify-keyspace-events "Ex".重启生效,索引位i的库,每当有过期的元素被删除时,向**key

  • koa2实现登录注册功能的示例代码

    本文介绍了koa2实现登录注册功能的示例代码,分享给大家,具体如下: 这个主要结合前几天的内容,做个实际案例的效果 版本: 项目结构: 前几天,我们把注册和登录的页面demo实现了,今天我们主要实现这么几个内容 注册新用户 判断该邮箱是否注册过 登录判断是否注册过 登录时的密码的正确 本文代码地址:https://github.com/xiaqijian/koa2-lessons/tree/master/lesson6 明天,我们将利用session实现登录状态判断 今天的这篇是在之前的代码基础

  • php+js实现点赞功能的示例详解

    最近在做一个视频网站,需要实现视频的点赞功能,我是结合ajax和数据库实现的,数据库的格式为有四个字段:文章id,赞,踩,ip.因为需要一个ip只能点赞一次,所以需要一个ip字段存储点赞的ip,这样便于判断该ip是否已经点赞过了: 我将点赞和踩的图片做成两个按钮:具体代码如下: <button style="margin-left:4px" id="vote" rel="<?php echo 文章id;?>"> <i

  • Mybatis Plus 自定义方法实现分页功能的示例代码

    一般物理分页,即通过sql语句分页,都是在sql语句后面添加limit分页语句,在xml文件里传入分页的参数,再多配置一条sql,用于查询总数: <select id="queryStudentsBySql" parameterType="map" resultMap="studentmapper"> select * from student limit #{currIndex} , #{pageSize} </select&

  • Flutter实现仿微信分享功能的示例代码

    目录 1.首先去pub官网 2 在微信开放平台注册开发者账号以及创建你的应用程序 3 在分享页面 3.1 初始化 3.2 检测微信是否安装 3.3 分享微信消息 总结 本文设计到的知识点有 主要问题 Flutter 用来快速开发 Android iOS平台应用,在Flutter 中,通过 fluwx或者fluwx_no_pay 插件来实现微信分享功能 主要还是看自己的需求,本示例我将按照没有支付的实现.至于为什么,主要是ios打包提审比较麻烦. 那么接下来就看一下如何实现吧, 1.首先去pub官

  • SpringBoot整合Redis实现访问量统计的示例代码

    目录 前言 Spring Boot 整合 Redis 引入依赖.增加配置 翠花!上代码 前言 之前开发系统的时候客户提到了一个需求:需要统计某些页面的访问量,记得当时还纠结了一阵子,不知道怎么去实现这个功能,后来还是在大佬的带领下借助 Redis 实现了这个功能.今天又回想起了这件事,正好和大家分享一下 Spring Boot 整合 Redis 实现访问量统计的全过程. 首先先解释一下为什么需要借助 Redis,其实原因也很简单,就是因为它非常快(每秒可执行大约110000次的 SET 操作,每

  • 利用Matlab绘制有趣图像的示例代码

    目录 1.随机樱花树 2.苹果绘制 3.南瓜绘制 4.一堆三角形绘制 5.月饼绘制 6.大钻石绘制 7.有趣曲线1 8.有趣曲线2 9.有趣曲线3——蝴蝶曲线 10.有趣曲线4——心形曲线 11.有趣曲线5 12.会害羞的含羞草 13.随机雪景 1.随机樱花树 function sakura % @author:slandarer % 随机形状二叉树樱花树绘制 hold on,axis equal axis(0.5+[-10,50,0,50]) set(gca,'xtick',[],'ytick

随机推荐