Redis点赞业务的设计与实现(Redis键值设计)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 案例分享Redis点赞业务实现!
【辰兮要努力】:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行!

博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、数据库、项目案例等相关知识点总结,感谢你的阅读和关注,希望我的博客能帮助到更多的人,分享获取新知,大家一起进步!

吾等采石之人,应怀大教堂之心,愿你们奔赴在各自的热爱中…

一、Redis序言

一年前就已经接触了Redis的相关知识,了解了这个强大的Redis缓存数据库,接下来会陆续抽时间整理Redis的相关具体应用在电影购票,点赞等相关场景的实现和Redis键值的设计思路,希望自己努力成为一名合格的开发工程师!


Redis简介

Redis 是开源免费的,遵守BSD协议,是一个高性能的key-value非关系型数据库


Redis特点:


1、Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。


2、Redis不仅仅支持简单的key-value类型的数据,同时还提供String,list,set,zset,hash等数据结构的存储。

3、Redis支持数据的备份,即master-slave模式的数据备份。

4、性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。

5、原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

6、丰富的特性 – Redis还支持 publish/subscribe, 通知, 设置key有效期等等特性。


二、Redis点赞

先说一下自己项目遇到的真实场景,就是给一个社交圈平台帖子点赞和评论点赞的Redis键值设计!

当我给 当前帖子(post)点赞后这个接口会有两个逻辑

第一:当前帖子总赞数+1 ,然后将新的点赞总数存储在redis中;

第二:用Redis记录用户 给这个帖子点赞

当我给 当前帖子某一条评论(review)点赞后这个接口会有两个逻辑

第一:当前评论总赞数+1 ,然后将新的点赞总数存储在redis中;

第二:用Redis记录用户 给这个该评论点赞


用户给帖子点赞这里的用户ID(userId)是从后台直接获取的,这里有一个逻辑就是:判断如果用户未登录是无法进行点赞的;

服务器开启postman测试

正常postman测试 传入点赞帖子ID(postId)和点赞状态likeStatus

测试数据给id为65的帖子点赞,传入1代表点赞 0代表取消赞

在这里插入图片描述

测试数据 给帖子Id为66号的帖子点赞 ,传入1代表点赞 0代表取消赞
在这里插入图片描述

这里我们登陆Redis的桌面工具查看我们存储redis键值的情况!
在这里插入图片描述

来看看显示一下存储效果,POSTID是存储点赞总数的,POST_USERID是记录用户点赞的,绑定的是用户id和帖子id

在这里插入图片描述
如上显示的是记录了65,66号帖子的点赞数,同时记录了34号用户点赞了65和66号帖子;


如上我们要设计两个键值:一个存储总点赞数目的键,一个记录具体用户点赞的键

    //单独记录帖子本身的键的设计
        String key = "POSTID:" + postId;
    //记录用户点赞具体帖子的键值设计
        String userKey = "POST_USERID:" + postId + "-" + userId;

键值这里是可以灵活设计的,但是你要确保唯一且自己能看懂


相关代码:点赞,要传入的是帖子id(postId) ,用户id (userId),用户点赞状态likeStatus 0代表取消,1代表点赞

简单逻辑就是先判断Redis中有无我们需要的键
1.如果有我们要的键,我们直接从键中取出对应的值 即(点赞总数 +用户点赞情况)
2.如果没有我们要的键,我们先从数据库中取点赞总数的情况,如果数据库中取出点赞总数为0,那么代表是第一次点赞,我们给他设置为1,并存储在对应的Redis中
3.针对具体的情况,再看看用户是点赞1还是取消赞0,再对应的+ -和增加删除

//点赞,要传入的是帖子id,用户id,用户点赞状态0代表取消,1代表点赞
    public void updateLike(Long postId, Long userId, Integer likeStatus) {
    //单独记录帖子本身的键的设计
        String key = "POSTID:" + postId;
    //记录用户点赞具体帖子的键值设计
        String userKey = "POST_USERID:" + postId + "-" + userId;
    //如果redis中有这个键
        if (redisTemplate.hasKey(key)) {
        //我们将相关的值取出来,这个值就是该帖子的点赞总数
            Object o = redisTemplate.opsForValue().get(key);
            if (o != null) {
           //取出点赞总数值,转换成int类型进行总数加减
                int likeNum = Integer.parseInt(o.toString());
                //如果用户点赞
                if (likeStatus == 1) {
                    try {
                        //逻辑1:帖子点赞总数+1,存储对应的键值,key为键,点赞后点赞总数加一存储
                        redisTemplate.opsForValue().set(key, likeNum + 1, 10L, TimeUnit.MINUTES);
                        //逻辑2:记录对用户的点赞情况,将用户和对应帖子绑定,在redis中存储对应的键
                        redisTemplate.opsForValue().set(userKey, 1, 1L, TimeUnit.DAYS);
                    } catch (Exception e) {
                        logger.error("redis error:{}", e);
                    }
              
                    // 写入数据库post_like,其实也可以在数据库中记录对应点赞情况,这样是防止点赞redis过期,具体业务具体分析;
                    //postLikeService.save(postId, userId);
          
                } else {
                    try {
                        redisTemplate.opsForValue().set(key, likeNum - 1, 10L, TimeUnit.MINUTES);
                        redisTemplate.delete(userKey);
                    } catch (Exception e) {
                        logger.error("redis error:{}", e);
                    }
                    postLikeService.delete(postId, userId);
                    //总赞数
                    postMapper.updatePostCountLike(postId, (long) (likeNum - 1));
                }
            }
        } else {
         /**
         这里是判断Redis中没有相关键值记录,即Redis中没有记录具体的帖子点赞总数和某一个用户的点赞情况。
         
         这里一般是两个情况
         1.该帖子从来没有被点赞过 这是第一次,第一次点赞的时候就会设置键值
         2.Redis中相关键值对过期
         **/
         
            // 首先我们查询数据库,拿取数据库的该帖子的点赞总数,这两步具体看业务需求
             Post post = postMapper.selectByPrimaryKey(postId);
             int likeCount = post.getLikeCount();

       //当用户点赞的时候
            if (likeStatus == 1) {
                try {
                //逻辑一:首先帖子点赞总数的存储在Redis中
                    redisTemplate.opsForValue().set(key, likeCount + 1, 10L, TimeUnit.MINUTES);
                //逻辑二:在Redis中记录某一个具体用户的点赞情况
                    redisTemplate.opsForValue().set(userKey, 1, 1L, TimeUnit.DAYS);
                 
                 //这个likecount是数据库取出来的,如果点赞总数为0,代表没有人点赞过,帖子点赞总数设置1
                    if (likeCount == 0) {
                        post.setLikeCount(1);
                      //同时同步更新到数据库数据表中  这一步更加你自己的业务需求走  简单的demo不需要
                        postMapper.updateByPrimaryKey(post);
                    }
                } catch (Exception e) {
                    logger.error("redis error:{}", e);
                }
              
                // 写入数据库post_like 按需求看自己是否要同步到数据库
                //postLikeService.save(postId, userId);
          
            } else {
                //如果取出来的点赞总数为0,点赞总数无法 -1 ,出现这样的情况是我postman测试直接传入likestatus为0的情况,真实页面如果没有点赞是无法传入0进来的;
                if (likeCount == 0) {
                    logger.error("点赞总数为0,无法取消点赞");
                } else {
                    try {
                    //
                        redisTemplate.opsForValue().set(key, likeCount - 1, 10L, TimeUnit.MINUTES);
                        redisTemplate.delete(userKey);
                    } catch (Exception e) {
                        logger.error("redis error:{}", e);
                    }
                  
                }
            }
        }
    }

如上的每一行我都写了注释,具体你应用到你的Redis中要根据项目具体的业务逻辑来,代码可以适当删减;

核心逻辑不变:设计一个存储总点赞数目的键,设计一个记录具体用户点赞的键,键值保持唯一且和具体的帖子对应

    //单独记录帖子本身的键的设计
        String key = "POSTID:" + postId;
    //记录用户点赞具体帖子的键值设计
        String userKey = "POST_USERID:" + postId + "-" + userId;

所以我们一般采用帖子ID (postId)来拼接一个字符串保持此键的唯一


如上是存储,你如果要显示给前端页面就用如下方式再取出来即可,Redis中也可以去除点赞总数;

            String userKey = "POST_USERID:" + postId + "-" + userId;
            boolean likeStatus = redisTemplate.hasKey(userKey);
            //从redis中判断用户是否喜欢帖子
            postVo.setLikeStatus(likeStatus);

想必你一定有了更深入的了解,我们下期Redis文章见……


The best investment is to invest in yourself.

在这里插入图片描述

愿你们奔赴在自己的热爱里!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
存储 缓存 NoSQL
深入解析Redis:一种快速、高效的键值存储系统
**Redis** 是一款高性能的键值存储系统,以其内存数据、高效数据结构、持久化机制和丰富的功能在现代应用中占有一席之地。支持字符串、哈希、列表、集合和有序集合等多种数据结构,适用于缓存、计数、分布式锁和消息队列等场景。安装Redis涉及下载、编译和配置`redis.conf`。基本操作包括键值对的设置与获取,以及哈希、列表、集合和有序集合的操作。高级特性涵盖发布/订阅、事务处理和Lua脚本。优化策略包括选择合适数据结构、配置缓存和使用Pipeline。注意安全、监控和备份策略,以确保系统稳定和数据安全。
328 1
|
8月前
|
NoSQL 关系型数据库 Go
更新Navicat Premium 16.2 之 如何使用Navicat连接Redis的新手教程《更新Navicat Premium 16.2并连接Redis:高效管理数据库和键值存储》
更新Navicat Premium 16.2 之 如何使用Navicat连接Redis的新手教程《更新Navicat Premium 16.2并连接Redis:高效管理数据库和键值存储》
539 0
更新Navicat Premium 16.2 之 如何使用Navicat连接Redis的新手教程《更新Navicat Premium 16.2并连接Redis:高效管理数据库和键值存储》
|
4天前
|
NoSQL Java Redis
Redis中的键值过期操作
Redis中的键值过期操作
|
1月前
|
存储 缓存 NoSQL
Redis进阶-Redis键值设计及BigKey问题
Redis进阶-Redis键值设计及BigKey问题
61 0
|
8月前
|
存储 缓存 NoSQL
Redis 基础知识和核心概念解析:理解 Redis 的键值操作和过期策略
Redis 基础知识和核心概念解析:理解 Redis 的键值操作和过期策略
85 1
|
7月前
|
JSON NoSQL Java
Redis键值:\xac\xed\x00\x05t\x00的解决
Redis键值:\xac\xed\x00\x05t\x00的解决
248 0
|
存储 NoSQL Redis
Redis中的哈希表的键名和键值是什么意思?分别有什么作用?底层原理是什么?
Redis中的哈希表的键名和键值是什么意思?分别有什么作用?底层原理是什么?
206 0
|
NoSQL Redis
Redis学习4:List数据类型、拓展操作、实现日志等
注意点:对存储空间的顺序进行分析!
Redis学习4:List数据类型、拓展操作、实现日志等
|
存储 NoSQL Redis
Redis学习3:hash类型操作、拓展操作、实现购物等
首先可以理解成一个redis里面有一个小的redis。同时要注意引入了一个field的名字。
Redis学习3:hash类型操作、拓展操作、实现购物等
|
缓存 NoSQL 安全
2021年你还不会Shiro?----10.使用redis实现Shiro的缓存
上一篇文章已经总结了使用ehCache来实现Shiro的缓存管理,步骤也很简单,引入依赖后,直接开启Realm的缓存管理器即可。如果使用Redis来实现缓存管理其实也是一样的,我们也是需要引入redis的依赖,然后开启缓存传入自定义的redis的缓存管理器就行。区别是我们需要为自定义的redis缓存管理器提供自定义的缓存管理类。这个缓存管理类中需要使用到redisTemplate模板,这个模板我们也是需要自己定义。
242 0
2021年你还不会Shiro?----10.使用redis实现Shiro的缓存