Java后端开发 ”Bug“ 分享——订单与优惠卷

“优惠券风波”:一段代码引发的线上事故

起因:优惠券功能上线

故事的开始源于公司新上线的一项促销活动——在用户未使用优惠券时,系统会自动赠送一张优惠券。这个功能不仅能提升用户体验,还能拉动平台的销售额。为了赶上活动上线时间,组长将这项任务交给了刚转正的开发小张。

小张心中既紧张又兴奋,立刻投入工作。经过一夜的奋战,当晚,他就提交了代码,并信心满满地对组长说:“放心吧,功能已经写好,测试通过,没有任何问题!”小张对自己的代码很满意,认为加锁和状态校验都已经足够周全。在他的测试环境里,功能表现一切正常,组长也批准了代码上线。

转折:生产环境的“优惠券雨”

功能上线后,活动初期数据非常亮眼——大量用户在收到优惠券后进行了消费。然而,短短几个小时后,运营部却收到了大量用户的投诉:有用户发现自己的账户里收到的优惠券数量成倍增长.

  • 有些用户账户里多出了数十张甚至上百张优惠券。
  • 数据库的CPU使用率飙升,写操作排队严重,导致整个系统几乎瘫痪。
  • 线上出现多起订单错误日志,运维团队忙得焦头烂额。

与此同时,后台的服务压力剧增,数据库的CPU使用率飙升,监控系统发出了一片告警声。

运维火速介入,更严重的是,因为系统的故障,公司损失了几笔大额订单,高层震怒,扬言如果问题不能解决,整个团队都可能面临解散的危机!

话不多说,直接上代码:

    public void getCouponsByOrder(String orderId) {
        //获取订单
        Order order = orderService.getOrderById(orderId);
        if(order == null){
            return ;
        }
        //加锁
        lock.lock();
        order = getOrderForUpdate(orderId);
        try{
            //判断订单是否有优惠券
            if(!order.getHasCoupons()){
                //如果没有使用优惠卷,系统默认赠送优惠卷
                couponsService.createCouponsAndSave(orderId);
            }
        }finally {
            lock.unlock();
        }
    }
危机:组内的混乱排查

团队进入了“战斗模式”。大家围绕这段代码进行分析,但一时半会儿谁也看不出问题。
“逻辑没问题啊,加了锁的,这不就是标准的并发处理方式吗?”小王一脸茫然。
“是不是数据库问题?或者是缓存同步有问题?”测试工程师提出猜测。
“这种锁到底管不管用?”运维开始怀疑架构本身。
大家讨论了整整两天,依然没有找到根本原因。

转机:老员工的登场

无奈之下,组长拨通了一个“传说中”的号码。这是团队里已经调岗的资深工程师老李,他曾是系统的核心开发者,对架构的每个细节了如指掌。

老李接到电话时,正在家中喂猫。听完情况后,他轻轻一笑:“这事儿,听起来有点意思。把代码发我看看。”

老李摇摇头:“这代码确实像个实习生写的,不过问题也不复杂,改改就行了。”

1. MySQL事务默认隔离级别
MySQL默认隔离级别:可重复读 (REPEATABLE READ)

MySQL默认的事务隔离级别是 可重复读 (REPEATABLE READ)。在这种隔离级别下,事务开始后,事务内的所有查询在同一事务中多次读取数据时,结果是一致的,即使其他事务对该数据进行了修改。为了实现这一点,MySQL通过 MVCC(多版本并发控制) 实现快照读。

问题分析:逻辑与隔离级别的交互

代码中的逻辑和隔离级别产生了以下几个潜在问题:

  1. 快照读导致状态不一致
    在调用 orderService.getOrderById(orderId) 时,读取的是快照数据。如果这时有其他事务更新了订单的 hasCoupons 状态,这些修改不会被当前事务看到。
  2. 加锁不生效
    MySQL的SELECT默认是快照读,只有显式使用 FOR UPDATE 或类似语法才能触发行锁。如果没有正确使用锁,即使在事务中操作,订单状态的并发修改也无法避免。
  3. 重复赠送优惠券
    当多个事务几乎同时执行,读取 hasCoupons 时可能都为 false,并发调用了 createCouponsAndSave(orderId),最终导致用户多次收到优惠券。
总的来说就是,当线程A,B进入事务时,生成了此刻的快照,然后A先获取到锁,A修改了数据,但是B此时已经生成了快照,所以B后面拿到的是之前的旧数据。
如何从隔离级别下手解决?

以下是从隔离级别与事务设计入手的解决方案:


改进方案

  1. 将数据库的隔离级别更改成读已提交,即可完美解决

    读已提交是在每一次select的时候生成快照

  2. 使用分布式锁。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot餐厅点餐系统丨源码+数据库+万字文档+PPT

作者简介: 作者:学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等 文末获取“源码数据库万字文档PPT”,支持远程部署调试、运行安装。 技术框架 开发语言:Java 框架:springbo…

ArkTs组件(2)

一.下拉列表组件&#xff1a;Select 1.接口 Select(options: Array<SelectOption>) 参数名类型必填说明optionsArray<SelectOption>是设置下拉选项。 SelectOption对象说明 名称类型必填说明valueResourceStr是 下拉选项内容。 iconResourceStr否 下拉选项图片…

【MATLAB第110期】#保姆级教学 | 基于MATLAB的PAWN全局敏感性分析方法(无目标函数)含特征变量置信区间分析

【MATLAB第110期】#保姆级教学 | 基于MATLAB的PAWN全局敏感性分析方法&#xff08;无目标函数&#xff09;含特征变量置信区间分析 一、介绍 PAWN&#xff08;Probabilistic Analysis With Numerical Uncertainties&#xff09;是一种基于密度的全局敏感性分析&#xff08;Gl…

请购单一直提示需求部门不能为空无法提交

终于发现了它的逻辑。用户很多次反馈&#xff0c;提交请购单时&#xff0c;提示需求部门不能为空&#xff0c;既使选择了需求部门&#xff0c;保存时&#xff0c;神奇的是会清空掉部门的信息&#xff0c;提交时就会有错误提示出来。 原因&#xff1a;光选择单头上的需求部门是…

leetcode 面试经典 150 题:矩阵置零

链接矩阵置零题序号73题型二维数组解题方法标记数组法难度中等熟练度✅✅✅✅ 题目 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1]…

AIGC:生成图像动力学

文章目录 前言一、介绍二、方法2.1、运动预测模块运动纹理 2.2、图像渲染模块 三、数据集实验总结 前言 让静态的风景图能够动起来真的很有意思&#xff0c;不得不说CVPR2024 best paper实质名归&#xff0c;创意十足的一篇文章&#xff01;&#xff01;&#xff01; paper&a…

python: Oracle Stored Procedure query table

oracel sql script CREATE OR REPLACE PROCEDURE SelectSchool(paramSchoolId IN char,p_cursor OUT SYS_REFCURSOR ) AS BEGINOPEN p_cursor FORSELECT *FROM SchoolWHERE SchoolId paramSchoolId; END SelectSchool; /-- 查询所有 CREATE OR REPLACE PROCEDURE SelectScho…

社区版Dify 轻松实现文生图,Dify+LLM+ComfyUI

社区版Dify 轻松实文生图&#xff0c;DifyLLMComfyUI Dify 安装可参考这里ComfyUI 其实 比 WebUI更简单更实用DifyComfyUIDifyLLM1. Qwen 通义千问大模型系列2. OpenAI大模型系列3. 本地Ollama搭建 DifyLLMComfyUI Dify 安装可参考这里 这是一个在Dify上实现 文生图的教程&…

Docker部署Sentinel

一、简介 是什么&#xff1a;面向分布式、多语言异构化服务架构的流量治理组件 能干嘛&#xff1a;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性 官网地址&#xff1a;https://sentinelguard.io/zh-c…

实用工具推荐----Doxygen使用方法

目录 目录 1 软件介绍 2 Doxygen软件下载方法 3 Doxygen软件配置方法 4 标准注释描述 4.1 块注释 和 特殊描述字符 4.1.1 函数描述示例 4.1.2结构体数组变量示例 特别注意&#xff1a; 4.2单行注释 4.2.1 单个变量注释示例 特别注意&#xff1a; 4.2.2对于枚举变量…

并发编程 - 死锁的产生、排查与解决方案

在多线程编程中&#xff0c;死锁是一种非常常见的问题&#xff0c;稍不留神可能就会产生死锁&#xff0c;今天就和大家分享死锁产生的原因&#xff0c;如何排查&#xff0c;以及解决办法。 线程死锁通常是因为两个或两个以上线程在资源争夺中&#xff0c;形成循环等待&#xf…

云轴科技ZStack获评OpenCloudOS社区2024年度优秀贡献单位

近日&#xff0c;由 OpenCloudOS 社区主办的 2024 OpenCloudOS 年会在北京成功召开。本次大会以“稳建基石&#xff0c;共创新篇”为主题&#xff0c;汇集了业界顶级技术专家与行业领袖&#xff0c;共同探讨下一代操作系统的建设与未来。云轴科技ZStack作为OpenCloudOS 社区的重…

clickhouse解决suspiciously many的异常

1. 问题背景 clickhouse安装在虚拟机上&#xff0c;持续写入日志时&#xff0c;突然关机&#xff0c;然后重启&#xff0c;会出现clickhouse可以正常启动&#xff0c;但是查询sql语句&#xff0c;提示suspiciously many异常&#xff0c;如图所示 2. 问题修复 touch /data/cl…

从零开始k8s-部署篇(未完待续)

从零开始k8s 1.部署k8s-部署篇 1.部署k8s-部署篇 本次部署完全学习于华子的博客点击此处进入华子主页 K8S中文官网&#xff1a;https://kubernetes.io/zh-cn 笔者从零开始部署的k8s&#xff0c;部署前置条件为 1.需要harbor仓库&#xff0c;存放镜像&#xff0c;拉取镜像&am…

Dots 常用操作

游戏中有多个蚂蚁群落&#xff0c;每个蚂蚁属于一个群落&#xff0c;如何设计数据结构&#xff1f; 方法1&#xff1a;为蚂蚁组件添加一个属性 ID&#xff0c;会造成逻辑中大量分支语句&#xff0c;如果分支语句逻辑不平衡可能带来 Job 调度问题&#xff0c;每个蚂蚁会有一份蚂…

如何通过 Kafka 将数据导入 Elasticsearch

作者&#xff1a;来自 Elastic Andre Luiz 将 Apache Kafka 与 Elasticsearch 集成的分步指南&#xff0c;以便使用 Python、Docker Compose 和 Kafka Connect 实现高效的数据提取、索引和可视化。 在本文中&#xff0c;我们将展示如何将 Apache Kafka 与 Elasticsearch 集成以…

深入浅出:AWT的基本组件及其应用

目录 前言 1. AWT简介 2. AWT基本组件 2.1 Button&#xff1a;按钮 2.2 Label&#xff1a;标签 ​编辑 2.3 TextField&#xff1a;文本框 2.4 Checkbox&#xff1a;复选框 2.5 Choice&#xff1a;下拉菜单 2.6 List&#xff1a;列表 综合案例 注意 3. AWT事件处理 …

Go Energy 跨平台框架 v2.5.1 发布

Energy 框架 是Go语言基于CEF 和 LCL 开发的跨平台 GUI 框架, 具体丰富的系统原生 UI 控件集, 丰富的 CEF 功能 API&#xff0c;简化且不失功能的 CEF 功能 API 使用。 特性&#xff1f; 特性描述跨平台支持 Windows, macOS, Linux简单Go语言的简单特性&#xff0c;使用简单…

JS 异步 ( 一、异步概念、Web worker 基本使用 )

文章目录 异步代码异步执行概念ES6 之前的异步 Web worker 异步 代码异步执行概念 通常代码是自上而下同步执行的&#xff0c;既后面的代码必须等待前面的代码执行完才会执行&#xff0c;而异步执行则是将主线程中的某段代码交由子线程去执行&#xff0c;当交给子线程后&…

机器学习(二)-简单线性回归

文章目录 1. 简单线性回归理论2. python通过简单线性回归预测房价2.1 预测数据2.2导入标准库2.3 导入数据2.4 划分数据集2.5 导入线性回归模块2.6 对测试集进行预测2.7 计算均方误差 J2.8 计算参数 w0、w12.9 可视化训练集拟合结果2.10 可视化测试集拟合结果2.11 保存模型2.12 …