当前位置: 首页 > news >正文

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. 使用分布式锁。

相关文章:

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

“优惠券风波”:一段代码引发的线上事故 起因:优惠券功能上线 故事的开始源于公司新上线的一项促销活动——在用户未使用优惠券时,系统会自动赠送一张优惠券。这个功能不仅能提升用户体验,还能拉动平台的销售额。为了赶上活动上…...

Linux系统之tee命令的基本使用

Linux系统之tee命令的基本使用 一、tee命令介绍二、tee命令的使用帮助2.1 tee命令的help帮助2.2 tee命令帮助解释 三、tee命令的基本使用3.1 写入文件3.2 追加文件3.3 结合sudo命令3.4 结合EOF使用 四、注意事项 一、tee命令介绍 tee 是 Linux 和 Unix 系统中的一个命令&#x…...

idea 8年使用整理

文章目录 前言idea 8年使用整理1. 覆盖application配置2. 启动的时候设置编辑空间大小,并忽略最大空间3. 查询类的关系4. 查看这个方法的引用关系5. 查看方法的调用关系5.1. 查看被调用关系5.2. 查看调用关系 6. 方法分隔线7. 选择快捷键类型8. 代码预览插件9. JReb…...

多个微服务 Mybatis 过程中出现了Invalid bound statement (not found)的特殊问题

针对多个微服务的场景,记录一下这个特殊问题: 如果启动类上用了这个MapperScan注解 在resource 目录下必须建相同的 com.demo.biz.mapper 目录结构,否则会加载不到XML资源文件 。 并且切记是com/demo/biz 这样的格式创建,不要使用…...

k8s,service如何找到容器

Kubernetes之所以需要Service,一方面是因为Pod的IP不是固定的,另一方面则是因为一组Pod实例之间总会有负载均衡的需求 被selector选中的Pod,就称为Service的Endpoints,查看方式: kubectl get endpoints hostnames需要…...

观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用?

大家好,我是锋哥。今天分享关于【观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用?】面试题。希望对大家有帮助; 观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用? 1000道 …...

docker compose deploy fate cluster

官方文档 写的不清晰 KubeFATE,用于生成部署脚本,链接 部署机就是下载了 KubeFATE的主机;运行机就是要安装fate容器的主机(部署机和运行机可以相同) 两个主机:并非必须 centos7,Ubuntu也行Doc…...

字节跳动Java开发面试题及参考答案(数据结构算法-手撕面试题)

怎么判断两个链表是否相交?怎么优化? 判断两个链表是否相交可以采用多种方法。 一种方法是使用双指针。首先分别遍历两个链表,得到两个链表的长度。然后让长链表的指针先走两个链表长度差的步数。之后,同时移动两个链表的指针,每次比较两个指针是否指向相同的节点。如果指…...

网工日记:FTP工作模式

FTP 基本概念 FTP(File Transfer Protocol)即文件传输协议,是用于在网络上进行文件传输的标准协议。它运行在 TCP/IP 协议栈之上,采用客户端 - 服务器(C/S)架构,通过在客户端和服务器之间建立控…...

unity使用代码在动画片段中添加event

unity使用代码在动画片段中添加event using UnityEngine;public static class AnimationHelper {/// <summary>/// 获取Animator状态对应的动画片段/// </summary>/// <param name"animator">Animator组件</param>/// <param name"…...

嵌入式轻量级开源操作系统:HeliOS的使用

嵌入式轻量级开源操作系统:HeliOS的使用 &#x1f4cd;项目地址&#xff1a;https://github.com/heliosproj/HeliOS HeliOS项目是一个社区交付的开源项目&#xff0c;用于构建和维护HeliOS嵌入式操作系统&#xff08;OS&#xff09;。HeliOS是一个功能齐全的操作系统&#xff0…...

解决VMware的ubuntu22虚拟机没有网络

解决步骤 1.在 Windows 系统中&#xff0c;按 “WinR” 键&#xff0c;输入 “services.msc” 并回车&#xff0c;在服务列表中找到 “VMware DHCP Service” 和 “VMware NAT Service”&#xff0c;确保这两个服务已启动&#xff0c;若未启动则右键点击选择 “启动”&#xf…...

金属衬底介质片对平面波的反射-问题的解析求解和FEM求解

金属衬底介质片对平面波的反射-问题的解析求解和FEM求解 参考有限元从零单排系列4 代码参考了上面大佬文章提供的&#xff0c;但是部分计算系数错了&#xff0c;我改了下加了许多注释&#xff0c;便于大家理解。 书籍参考的电磁场有限元方法(金建铭)&#xff0c;所用的公式都…...

2023 年 9 月青少年软编等考 C 语言四级真题解析

目录 T1. 酒鬼T2. 大盗T3. 核电站思路分析T4. 盒子与小球之二思路分析T1. 酒鬼 此题为 2021 年 3 月四级第一题原题,见 2021 年 3 月青少年软编等考 C 语言四级真题解析中的 T1。 T2. 大盗 此题为 2021 年 6 月四级第二题原题,见 2021 年 6 月青少年软编等考 C 语言四级真…...

C++的内存四区

文章目录 内存四区1.程序运行前1.1 代码区2.1 全局区2.2 示例 2.程序运行后1.1 栈区1.2 堆区 内存四区 1.程序运行前 在程序编译后&#xff0c;生成了exe可执行程序&#xff0c;未执行该程序前分为两个区域。该区域的数据在程序结束后由操作系统释放. 1.1 代码区 ​存放 CPU …...

Java爬虫技术:按关键字搜索VIP商品详情

在数字化时代&#xff0c;电子商务平台的竞争日益激烈&#xff0c;而精准的数据采集和分析成为了企业获取竞争优势的关键。对于电商平台而言&#xff0c;能够根据用户输入的关键字快速搜索并展示VIP商品的详细信息&#xff0c;不仅能够提升用户体验&#xff0c;还能够增加销售机…...

C++ —— 模板类与函数

C —— 模板类与函数 模板类可以用于函数的参数和返回值&#xff0c;有三种形式&#xff1a; 普通函数&#xff0c;参数和返回值是模板类的实例化版本。函数模板&#xff0c;参数和返回值是某种的模板类。函数模板&#xff0c;参数和返回值是任意类型&#xff08;支持普通类和…...

【软考高级】系统架构设计师复习笔记-精华版

文章目录 前言0 系统架构设计师0.1 考架构还是考系分0.2 架构核心知识0.3 架构教材变化 1 计算机操作系统1.1 cpu 组成1.2 内核的五大功能1.3 流水线技术1.4 段页式存储1.5 I/O 软件1.6 文件管理1.7 系统工程相关 2 嵌入式2.1 嵌入式技术2.2 板级支持包&#xff08;BSP&#xf…...

免费 IP 归属地接口

免费GEOIP&#xff0c;查询IP信息&#xff0c;支持IPV4 IPV6 ,包含国家地理位置&#xff0c;维度&#xff0c;asm,邮编 等&#xff0c;例如 例如查询1.1.1.1 http://geoip.91hu.top/?ip1.1.1.1 返回json 对象...

AIA - IMSIC之二(附IMSIC处理流程图)

本文属于《 RISC-V指令集基础系列教程》之一,欢迎查看其它文章。 1 ​​​​​​​通过IMSIC接收外部中断的CSR 软件通过《AIA - 新增的CSR》描述的CSR来访问IMSIC。 machine level 的 CSR 与 IMSIC 的 machine level interrupt file 可相互互动;而 supervisor level 的 CSR…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...