JAVA项目点赞功能如何实现?如何利用缓存优化?如何防止刷赞?

- 普通的点赞如何实现? -

    每个人都见过点赞功能,大家想实现一个点赞功能也简单,比如一个简单的文章点赞逻辑如下:

    首先需要建个表,记录下点赞人的id,被点赞文章的id,点赞状态三个关键因素即可,需要的话可以把被点赞文章的作者id也冗余进来

冗余一个作者id字段这样子可以避免一个连表查询操作,在较大数据量下的时候,冗余字段带来的内存消耗的性价比是远远高于连表查询时带来的时间消耗的性价比的

    然后点赞的时候前端可以直接做一个状态的更改,无需等待后端结果返回,毕竟这是一个对数据精确性要求没那么高的功能

首先需要两张数据库表:

article文章表

article_user_thumb文章用户点赞关系表,用于记录哪个用户点赞了哪个文章:

后端逻辑的实现

查询数据库article表给已有点赞数进行+1操作

问题:

注意,这里可能有线程安全性问题,实际上这是一个并发问题,只要在并发的情况下就会出现问题,我们知道Spring Mvc是基于servlet的,servlet在接收到用户请求后会从线程池中拿一个线程分配给它,每个请求都是一个单独的线程。试想一下,如果A线程在执行完查询操作后,发现没有记录,随后由于CPU调度,把控制权让了出去,然后B线程执行查询,也发现没有记录,这时候A和B线程都会执行保存并商品点赞数加1这个操作,导致数据不正确。

问题解决:

(其实上述问题主要的出现的原因在于将查询和加1操作进行了分步,而没有做到原子操作)

那么我们可以通过以下代码 / SQL实现一个原子的累加操作:

productService.update(new UpdateWrapper<article>().lambda().setSql("like_count = like_count + 1").eq(Article::getAuthorId, authorId));= UPDATE article SET like_count = like_count + 1 WHERE author_id = ?;

因为在MySQL中执行DML操作的时候会自动上一个行级锁(前提是条件字段是被索引修饰),这样子可以保证其的SQL原子性

 新增article_user_thumb表的一条数据进行关联


缓存性能优化

为了避免频繁地访问数据库,可以使用缓存技术Redis,将点赞量存储在缓存中。每次用户点赞时,首先将点赞量从缓存中读取,然后对其进行修改(该两部分操作使用lua脚本实现,保证整体操作的原子性),最后再将修改后的点赞量写回缓存。设置定时任务异步的将redis中的数据持久化到mysql

 至于后端的处理逻辑,可以根据(点赞人的id,被点赞文章的id,点赞状态)三个字段,直接存到redis,做一个快速的读写,之后再异步保存到数据库中做一个备份即可(如果后续需要在别的地方如列表页查询,甚至可以直接在这里写到索引中)

总之的原则就是,数据的快速读写,允许一定时间的误差(比如我点完赞非常快速的刷新页面发现还是未点赞状态,再次刷新的时候就是已点赞状态这种现象,是可以容忍的,虽然它也几乎不可能出现)

相关的数据都可以存到redis,比如对某文章的点赞数

某个用户对某篇文章的点赞关系

 

还有一些逻辑可以做判断优化,比如可以通过redis中的数据判断用户是否重复点赞,如果是的话就没必要再访问一次数据库了 


防止刷赞

防刷策略:

为了防止刷赞,可以实施以下几种策略:

  • 限制点赞频率:可以限制用户在一定时间内的点赞次数,例如,每分钟或每小时点赞次数的限制。
  • IP限制:可以限制同一IP地址下的点赞操作次数或频率,以防止刷赞行为。
  • 用户认证:确保只有已登录的用户才能进行点赞操作,减少匿名用户的刷赞可能性。
  • 验证码验证:在点赞操作前,要求用户进行验证码验证,以确保每个点赞请求都是由真实用户发起的。

自定义注解实现综合防刷策略

可能不同的产品对此的方案也不一样,如果可以在网关做限流那是最好的,但是有些具体的业务在网关配置还是不太方便的

    下面给大家一个简单使用的防止刷赞的限流方法

首先定义一个注解@Limit

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Limit {/*** 资源的名字** @return String*/String name() default "";/*** 资源的key** @return String*/String key() default "";/*** Key的prefix** @return String*/String prefix() default "";/*** 给定的时间段* 单位秒** @return int*/int period();/*** 最多的访问限制次数** @return int*/int count();/*** 类型** @return LimitType*/LimitType limitType() default LimitType.CUSTOMER;// 限流方式,默认根据方法名methodName限流enum LimitType {/*** 自定义key*/CUSTOMER,/*** 根据请求者IP*/IP}
}

它可以支持自定义key、ip限流以及根据方法名三种方式进行接口访问频次限流

    然后是注解拦截内容的处理逻辑,这部分代码太长就不贴了,贴一下主要逻辑

主要的逻辑方法

@Around("execution(public * *(..)) && @annotation(com.luhui.utils.annotation.Limit)")public Object interceptor(ProceedingJoinPoint pjp) {MethodSignature signature = (MethodSignature) pjp.getSignature();Method method = signature.getMethod();String[] paramNames = signature.getParameterNames();Stream<?> stream = ArrayUtils.isEmpty(pjp.getArgs()) ? Stream.empty() : Arrays.stream(pjp.getArgs());List<Object> paramValues = stream.filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse))).collect(Collectors.toList());Limit limitAnnotation = method.getAnnotation(Limit.class);Limit.LimitType limitType = limitAnnotation.limitType();String key;int limitPeriod = limitAnnotation.period();int limitCount = limitAnnotation.count();switch (limitType) {case IP:key = getIpAddress();break;case CUSTOMER:key = getLimitKeyValue(limitAnnotation.key(), paramNames, paramValues);break;default:key = StringUtils.upperCase(method.getName());}ImmutableList<String> keys = ImmutableList.of(StringUtils.join(limitAnnotation.prefix(), key));try {String luaScript = buildLuaScript();RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);Number count = redisTemplate.execute(redisScript, keys, String.valueOf(limitCount), String.valueOf(limitPeriod));logger.info("Access try count is {} for name={} and key = {}", count, limitAnnotation.name(), key);if (count != null && count.intValue() <= limitCount) {return pjp.proceed();} else {return ApiResponse.fail("访问太频繁,请稍后再试");}} catch (Throwable e) {if (e instanceof RuntimeException) {throw new RuntimeException(e.getLocalizedMessage());}throw new RuntimeException("server exception");}}

 具体的频次控制是通过redis的lua表达式来实现的。

    使用起来也很方便,比如我要限制点赞接口5分钟内不能超过10次,直接在接口上加注解即可

@Limit(key = "uid;assetsId", period = 300, count = 10, name="like", prefix = "limit_")

    最后就可以实现啦!

大家如果觉得有帮助,欢迎关注我!你们的关注就是我的动力,有什么好的建议也可以留言

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/98354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【stm32芯片设置解惑】:stm32F103系列的开漏输出和推挽输出的区别

场景&#xff1a; 大家在开发stm32的时候&#xff0c;不管是标准库开发还是hal库开发&#xff0c;最基础的就是芯片引脚的某某设置&#xff0c;为什么这么设置&#xff1f;这样设置的好处是什么&#xff1f; 问题描述 — 开漏输出和推挽输出的用处和区别 什么是开漏输出&#x…

FPGA实现HDMI输入转SDI视频输出,提供4套工程源码和技术支持

目录 1、前言免责声明 2、我目前已有的SDI编解码方案3、设计思路框架核模块解析设计框图IT6802解码芯片配置及采集ADV7611解码芯片配置及采集silicon9011解码芯片配置及采集纯verilog的HDMI 解码模块RGB888转YUV422SPMTE编码SDI模式图像缓存SPMTE SDIGTXGV8500 4、vivado工程1-…

途虎养车上市、京东养车“震虎”,如何突围汽车后市场?

“汽车后市场第一股”终于来了&#xff01; 赶在十一黄金周之前&#xff0c;途虎养车股份有限公司(09690.HK&#xff0c;下称“途虎养车”)于9月26日挂牌港交所&#xff0c;开盘价为28港元/股&#xff0c;与发行价持平&#xff1b;IPO首日报收29.50港元/股&#xff0c;涨幅5.3…

机器学习笔记 - 基于pytorch、grad-cam的计算机视觉的高级可解释人工智能

一、pytorch-gradcam简介 ​Grad-CAM是常见的神经网络可视化的工具,用于探索模型的可解释性,广泛出现在各大顶会论文中,以详细具体地描述模型的效果。Grad-CAM的好处是,可以在不额外训练的情况下,只使用训练好的权重即可获得热力图。 1、CAM是什么? CAM全称Class Activa…

在PyCharm中添加anaconda环境

本文略过创建anaconda环境的部分~ 下文默认anaconda环境已经创建好 1. 点击新建项目 2. 1&#xff09;修改项目名称 2&#xff09;选择 “Previously configured interpreter” 曾经配置的解释器 3&#xff09;选择“Add Interpreter” 添加新的解释器 3. 选择 “A…

asp.net电影院选座系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net电影院选座系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言开发 asp.net电影院选座系统1 二、功能介…

记录极致CMS非富文本标签调用不改变格式

问题 在前台如何输出这三行是换行的 前台调用{$jz[hhl]}就变成这样了“这是第一行这是第二行这是第三行” 除了富文本还有没有什么可以实现这样的呢&#xff1f; 方法 {foreach explode("\n",$jz[hhl]) as $v} {if($v)} {$v} {/if} {/foreach}

航拍飞行器经营商城小程序的作用是什么

航拍人群越来越越多&#xff0c;一款靠谱的装备往往能达到预期效果&#xff0c;随着互联网信息传播度加深&#xff0c;也吸引了大批同样的爱好者加入航拍序列。 对航拍飞行器企业/经营商来说&#xff0c;市场增幅下也带来了不少商机&#xff0c;然在实际销售及客户赋能方面还是…

常用排序算法详解

1.冒泡排序原理示例代码实现 2.快速排序原理示例代码实现 3.插入排序原理示例代码实现 4.希尔排序原理示例代码实现 5.选择排序原理示例代码实现 6.堆排序原理示例代码实现 7.归并排序原理示例代码实现 本文讲述了常见的排序算法的执行过程&#xff0c;有详细实现过程举例 1.冒…

git+码云提交PR流程记录

前提条件&#xff1a;注册码云账号&#xff0c;本地安装git 如果不知道怎么注册和安装&#xff0c;可以参考gitgitee入门教程&#xff08;https://bbs.huaweicloud.com/forum/thread-55222-1-1.html&#xff09; 登录自己的码云账号 登陆了之后&#xff0c;在码云上打开目标项…

项目需求分析5大常见问题及解决方案

需求分析过程中&#xff0c;往往容易导致需求不准确和不完整&#xff0c;引起需求频繁变更&#xff0c;导致项目进度延误和成本增加&#xff1b;而需求分析的误解问题&#xff0c;导致交付产品无法满足客户期待&#xff0c;降低用户满意度和资源浪费。 那么在需求分析中&#x…

力扣第404题 左叶子之和 c++ 递归 与 迭代解法

题目 404. 左叶子之和 简单 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24示例 2: 输…

asp.net饭店订餐管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio计算机设计定制

一、源码特点 asp.net 饭店订餐管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语 言开发 asp.net饭店订餐系统 二、功能介…

设计模式 - 访问者模式

目录 一. 前言 二. 实现 三. 优缺点 一. 前言 访问者模式&#xff0c;即在不改变聚合对象内元素的前提下&#xff0c;为聚合对象内每个元素提供多种访问方式&#xff0c;即聚合对象内的每个元素都有多个访问者对象。访问者模式主要解决稳定的数据结构和易变元素的操作之间的…

Unity实现设计模式——策略模式

Unity实现设计模式——策略模式 策略模式是一种定义一些列算法的方法&#xff0c;这些所有的算法都是完成相同的工作&#xff0c;只是实现不同。它可以通过相同的方式调用所有的算法&#xff0c;减少各种算法类与使用算法类之间的耦合。 策略模式的 Strategy 类层次为 Contex…

放大招,百度文心大模型4.0正在加紧训练,即将发布

插播一条快讯&#xff01; &#xfeff;&#xfeff;刚刚看到一篇报道&#xff0c;说百度正在加紧训练文心大模型4.0&#xff01;百度5月发布了文心大模型3.5&#xff0c;才4个多月又要发布4.0了&#xff0c;这迭代速度简直了。据说这次发布将在10月17日百度世界大会上进行&am…

NeurIPS 2023 | 李飞飞团队提出SiamMAE:孪生掩码自编码器,刷榜视觉自监督方法

在计算机视觉领域&#xff0c;想要建立图像和场景&#xff08;scene&#xff09;之间之间的对应关系是一项比较困难的任务&#xff0c;尤其是在存在遮挡、视角改变或是物体外观发生变化的情况下。 最近&#xff0c;斯坦福大学李飞飞团队对MAE进行扩展&#xff0c;提出了孪生掩…

Tensorflow入门之 Hello World

Tensorflow入门之 Hello World 简介 Tensorflow 是 Google 开源的深度学习框架&#xff0c;来自于 Google Brain 研究项目&#xff0c;在 Google 第一代分布式机器学习框架 DistBelief 的基础上发展起来。 Tensorflow 的官方网址 http://www.tensorflow.org Tensorflow 的 G…

基于边缘网关的智慧工地监测方案

边缘物联网技术为千行百业赋能&#xff0c;依托边缘计算的低延时、高效率、广适用优势&#xff0c;也为工程建设产业带来新的增长动力。 例如在智慧工地应用中&#xff0c;围绕建设施工过程中的人员、设备、环境等要素&#xff0c;利用边缘计算网关构建全面的数据采集、分析、联…

web3.0时代分布式网络协议的异同

Web3.0时代标志着分布式网络协议的兴起&#xff0c;其中IPFS&#xff08;InterPlanetary File System&#xff09;和NDN&#xff08;Named Data Networking&#xff09;是备受瞩目的项目。尽管它们都属于分布式网络协议领域&#xff0c;但在多个方面存在显著区别。以下是IPFS和…