因为锁的问题,我们被扣了1万
前言
春节放假期间,一个项目上的积分接口被刷,而且不止一个人在刷,并且东西也被兑走,放假晚上被人叫起来排查问题,通过这个人的积分明细观察,基本一秒就能获取一次,远远超过了积分规则限定的次数,这肯定是用脚本了,虽然后期联系死活说自己是正常途径获取。由于是业主,我们还是决定自己来承担这个损失,被项目方从合同中扣除奖品费用1万余元。
问题原因
先说下接口的逻辑层次结构:
–controller 积分获取接口,用PointController表示
–service 积分获取接口service,用PointService表示
我用伪代码来表示整个调用逻辑:
PointController
@RestController
public class PointController {@Resourceprivate PointService pointService;@GetMapping(/addPoint)public Response addPoint() {//分布式锁,使用redis的NX命令RedisDistributedLock lock = new RedisDistributedLock();//创建一个3s过期,100ms休眠的锁if(lock.lock("POINT_KEY", 3000L, 100L)) {try {//调用pointService.addPoint();} catch (Exception ex) {e.printStackTrace;} finally {//解锁lock.unlock("POINT_KEY");}}return Response.ok(getLastPoint());}
}
- 创建一个分布式锁对象,该分布式锁使用redis的
NX
命令实现 - 随后创建一个3s过期的分布式锁,以便锁住该新增积分的请求
- 最后在新增积分执行完后,在finally中释放锁
- 最后返回该用户的最终积分
PointService
public class PointService {@Transactional(rollbackFor = Exception.class)public void addPoint() {//查询积分规则PointRule pointRule = getPointRule();//查询用户该积分项的积分获取记录总数Integer total = getPointRecords();//判断该用户的积分记录总数是否大于 积分规则限定的次数//大于则不处理,返回if(total - pointRule.getRuleTimes >= 0) {return;}//生成积分记录int insert = insertPointRecords();//更新用户总积分if(insert > 0) {updateUserPoint();}}
}
PointService 中的添加积分逻辑:
- 首先查询该项目积分规则,查询用户该积分项的积分获取记录总数
- 判断该用户的积分记录总数是否大于 积分规则限定的次数,大于则不处理,返回
- 生成积分记录
- 更新用户总积分
该添加积分的逻辑整体上看好像没什么问题,也确实在一切正常的情况下运行是不会有问题的。
如果PointService 中的添加积分逻辑在分布式锁有效期3s内执行完,是不会有问题的。
但如果PointService中的添加积分逻辑超过3s…那是不是后续请求又可以获取锁了,这也正是这次事故的原因。
因为PointService中的添加积分逻辑超过了3s,并且上一个请求的事务还未提交,后续请求已经获取锁进入PointService,在查询积分记录后,判断还是满足规则,继续执行后续的逻辑,造成用户能够获取多次积分。
问题处理
原因总结一下:
- 添加积分逻辑处理时间过长
- 分布式锁超时
第一个问题:逻辑改动过大,需要时间调整,没有采用
第二个问题:换成Redisson,因为redisson在即使超时的情况下也会续锁,避免锁超时
总结
一方面因为忙于做项目,忽略了代码Review,另一方面测试的时候没有对接口进行并发测试,或者根本没有测出来,第三没有监控工具监控长事务,以及频繁请求。
作者其他文章:
《Prometheus+Grafana 实践派》专栏火热更新中
- Grafana 的介绍和安装
- Grafana监控大屏配置参数介绍(一)
- Grafana监控大屏配置参数介绍(二)
- Grafana监控大屏可视化图表
- Grafana 查询数据和转换数据
- Grafana 告警模块介绍
- Grafana 告警接入飞书通知
Spring Boot Admin 系列
- Spring Boot Admin 参考指南
- SpringBoot Admin服务离线、不显示健康信息的问题
- Spring Boot Admin2 @EnableAdminServer的加载
- Spring Boot Admin2 AdminServerAutoConfiguration详解
- Spring Boot Admin2 实例状态监控详解
- Spring Boot Admin2 自定义JVM监控通知
- Spring Boot Admin2 自定义异常监控
- Spring Boot Admin 监控指标接入Grafana可视化
相关文章:
因为锁的问题,我们被扣了1万
前言 春节放假期间,一个项目上的积分接口被刷,而且不止一个人在刷,并且东西也被兑走,放假晚上被人叫起来排查问题,通过这个人的积分明细观察,基本一秒就能获取一次,远远超过了积分规则限定的次…...

【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)
【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER) 前文: blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录…...
浏览器渲染中的相关概念
渲染 渲染流水线 构建 DOM 树 输入:HTML 文档;处理:HTML 解析器解析;输出:DOM 数据解构。 样式计算 输入:CSS 文本;处理:属性值标准化,每个节点具体样式(…...
【MySQL】数据类型
1、数据类型描述 类型类型举例整数类型TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT浮点类型FLOAT、DOUBLE定点数类型DECIMAL位类型BIT日期时间类型YEAR、TIME、DATE、DATETIME、TIMESTAMP文本字符串类型CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT枚举类…...

L2-037 包装机
一种自动包装机的结构如图 1 所示。首先机器中有 N 条轨道,放置了一些物品。轨道下面有一个筐。当某条轨道的按钮被按下时,活塞向左推动,将轨道尽头的一件物品推落筐中。当 0 号按钮被按下时,机械手将抓取筐顶部的一件物品&#x…...

MySQL -查询日志、二进制日志、错误日志、慢查询日志
文章目录1.错误日志2.二进制日志3.查询日志4.慢查询日志1.错误日志 错误日志是 MySOL中最重要的日志之一,它记录了当 mvsald 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息当数据库出现任何故障导致无法正常使用时,建议…...

TCP实现可靠传输的实现
TCP实现可靠传输的实现 目录TCP实现可靠传输的实现ARQ协议停止等待协议(古老)连续ARQ协议累计重传(回退N帧的ARQ协议)缓存确认(选择重传ARQ协议)超时重传的时间选择TCP的流量控制零窗口探测报文段Nagle算法…...
2/14考试总结
时间安排 7:30–7:50 看题,T1可能是个数据结构之类的东西,T2是 dp ,T3 构造。 7:50–8:20 T3,仿照样例的构造,可以通过一部分测试点。 8:20–9:20 T1,发现题目实际上要求子树内各儿子的深度信息,可以 dsu ,对于不能暴…...

程序环境和预处理详解
文章目录一、程序环境1.1 - 翻译环境1.1.1 - 编译1.1.1.1 - 预编译(预处理)1.1.1.2 - 编译1.1.1.3 - 汇编1.1.2 - 链接1.2 - 执行环境二、预处理详解2.1 - 预定义符号2.2 - #define2.2.1 - #define 定义标识符2.2.1.1 - 语法2.2.1.2 - 建议2.2.2 - #defi…...

The Social-Engineer Toolkit(社会工程学工具包)互联网第一篇全模块讲解
一、工具介绍 Social-Engineer Toolkit 是一个专为社会工程设计的开源渗透测试框架,可以帮助或辅助你完成二维码攻击、可插拔介质攻击、鱼叉攻击和水坑攻击等。SET 本身提供了大量攻击选项,可让您快速进行信任型攻击,也是一款高度自定义工具…...

Windows11去掉不满足系统要求的提示水印
我的电脑是LEGION的拯救者R70002021,预装的是Windows 11 家庭中文版,没有折腾重装过系统,今天突然注意到右下角出现了这个提示:“不满足系统要求。转到’设置"了解详细信息”。 在进入设置 - 系统 面板中也提示不满足系统要…...

JavaScript 计时事件
JavaScript 计时事件 通过使用 JavaScript,我们有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。我们称之为计时事件。 在 JavaScript 中使用计时事件是很容易的,两个关键方法是: setInterval() - 间隔指定的…...

七大排序算法的多语言代码实现
文章目录 前言 一、排序算法 1.原理简述 2.分类与复杂度 二、实例代码 1.冒泡排序 C Python Java Golang Rust Dephi 2.选择排序 C Python Java Golang Rust Dephi 3.插入排序 C Python Java Golang Rust Dephi 4.希尔排序 编辑 C Python Java Gola…...
【基础算法】表达式计算
中缀表达式:我们平常见到的正常数学式子 后缀表达式:12-3* 后缀表达式对于计算机很容易计算,只需要从头部扫描字符串。然后遇到数字就入栈,遇到运算符就取出栈顶的两个数进行运算。最后把运算结果入栈,最后栈中就会剩一个数为答…...

动态规划问题
目录 一、动态规划简介 二、利用动态规划解决问题 1、斐波拉契序列 2、拆分词句 3、三角形最小路径和 4、不同的路径数目(一) 5、带权值的最小路径和 6、求路径ii 7、01背包 8、不同子序列 9、编辑距离 10、分割回文串 一、动态规划…...

【MySQL进阶】 存储引擎 索引
😊😊作者简介😊😊 : 大家好,我是南瓜籽,一个在校大二学生,我将会持续分享Java相关知识。 🎉🎉个人主页🎉🎉 : 南瓜籽的主页…...

5 款最好的免费 SSD 数据恢复软件
SSD(固态硬盘)提供比传统硬盘更快的读/写速度,使启动、软件加载和游戏启动更快。因此,在我们选择存储设备时,它是一个极好的选择。但是,它仍然存在数据丢失的风险。假设您是受害者之一,正在寻找…...

MyBatis案例 | 使用映射配置文件实现CRUD操作——删除数据
本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等,如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址:🔥JavaWeb Java入门篇: 🔥Java基础学习篇 Java进阶学习篇&…...
CSDN 编程竞赛二十八期题解
竞赛总览 CSDN 编程竞赛二十八期:比赛详情 (csdn.net) 本期竞赛的题目都很简单,但是非常考验读题和编码速度。这一次没有遇到bug,竞赛体验较好。 竞赛题解 题目1、小Q的鲜榨柠檬汁 团建活动是大家所想要的。小Q给大家准备了鲜橙汁。现在…...
DML数据操纵语言
DML数据操纵语言 目录概述一、插入语句(一)方式一(二)方式二:(三)两种方式的比较二、修改语句三、删除语句概述方式一:delete方式二:truncate语句 【清空语句】delete VS truncate 【面试题!!!】概述 数据…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用
阻止除自定义标签之外的所有标签 先输入一些标签测试,说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时(如通过点击或键盘导航&…...

Java中HashMap底层原理深度解析:从数据结构到红黑树优化
一、HashMap概述与核心特性 HashMap作为Java集合框架中最常用的数据结构之一,是基于哈希表的Map接口非同步实现。它允许使用null键和null值(但只能有一个null键),并且不保证映射顺序的恒久不变。与Hashtable相比,Hash…...
C#最佳实践:为何优先使用as或is而非强制转换
C#最佳实践:为何优先使用as或is而非强制转换 在 C# 的编程世界里,类型转换是我们经常会遇到的操作。就像在现实生活中,我们可能需要把不同形状的物品重新整理归类一样,在代码里,我们也常常需要将一个数据类型转换为另…...