秒杀系统解决两个核心问题的思路方法总结:1.库存超卖问题;2.用户重复抢购问题。
秒杀系统解决两个核心问题
- 秒杀系统解决两个核心问题:
- 一、解决库存超卖的核心逻辑:
- 解释:
- 原子性保证:
- 二、如何避免重复抢购:
- 使用 Redis 做唯一标识判断
- 优点:
- 三、流程完整梳理:
- 四、通过数据库建立唯一索引避免(用户重复抢购)
- 原因背景
- 如何通过数据库唯一索引避免重复下单?
- 程序中如何体现这一机制?
- 配合使用 Redis + 唯一索引实现双重保障
秒杀系统解决两个核心问题:
- 库存超卖问题
- 用户重复抢购问题
一、解决库存超卖的核心逻辑:
关键点:数据库层面的原子性更新 + 乐观锁判断库存是否充足
seckillGoodsService.update(new UpdateWrapper<SeckillGoods>().set("stock_count", seckillGoods.getStockCount()).eq("id", seckillGoods.getId()).gt("stock_count", 0)
);
解释:
eq("id", ...): 保证更新的是这条商品记录。gt("stock_count", 0): 加了一条库存大于 0 的条件,防止库存为 0 仍被减。
这个 update() 方法只有在满足条件(也就是库存大于 0)时才会成功返回 true,否则不更新。
原子性保证:
这个 UPDATE 操作由数据库完成,是原子性的,同时加了条件判断,避免并发环境下出现超卖问题。
二、如何避免重复抢购:
使用 Redis 做唯一标识判断
String seckillOrderJson = (String) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
if (!StringUtils.isEmpty(seckillOrderJson)) {return RespBean.error(RespBeanEnum.REPEATE_ERROR);
}
优点:
- Redis 访问速度极快,适合做高并发下的抢购标记。
- 提前判断用户是否已抢过,无需再查数据库,提高性能。
三、流程完整梳理:
用户请求 /seckill/doSeckill 接口↓
校验用户是否登录↓
查询商品库存(goodsService.findGoodsVoByGoodsId)↓
判断库存是否为 0↓
从 Redis 判断用户是否已经下过单(防止重复抢购)↓
调用 service 层下单逻辑(orderService.seckill)↓① 减库存(update + gt 判断)② 生成订单、秒杀订单入库③ 将订单信息写入 Redis 标记用户已抢购↓
返回下单成功 / 失败信息
四、通过数据库建立唯一索引避免(用户重复抢购)
在这个秒杀系统中,其实是非常关键的一步,是防止同一用户对同一商品生成多个秒杀订单的最终兜底措施。
原因背景
在高并发环境下,即使你在代码中已经通过 Redis 判断是否重复下单:
String seckillOrderJson = (String) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);
if (!StringUtils.isEmpty(seckillOrderJson)) {return RespBean.error(RespBeanEnum.REPEATE_ERROR);
}
由于 Redis 与数据库之间存在一定的 时延,仍可能出现“并发抢购成功但生成了两个订单”的情况 —— 即所谓的并发穿透检查逻辑。
如何通过数据库唯一索引避免重复下单?
seckill_order 表结构如下:
CREATE TABLE seckill_order (id BIGINT PRIMARY KEY AUTO_INCREMENT,user_id BIGINT NOT NULL,goods_id BIGINT NOT NULL,order_id BIGINT NOT NULL,-- 其他字段 ...UNIQUE KEY uniq_user_goods (user_id, goods_id)
);
关键:
UNIQUE KEY uniq_user_goods (user_id, goods_id)
它的作用是:同一个用户,对同一件商品,只能有一条秒杀订单记录。
也就是说,如果你尝试插入相同 user_id 和 goods_id 的数据,就会违反唯一索引,导致 SQL 执行失败。
程序中如何体现这一机制?
在 OrderServiceImpl.java 的代码中:
SeckillOrder seckillOrder = new SeckillOrder();
seckillOrder.setOrderId(order.getId());
seckillOrder.setUserId(user.getId());
seckillOrder.setGoodsId(goods.getId());
seckillOrderService.save(seckillOrder); // 插入数据库
这个 save 方法其实最终是调用 MyBatis-Plus 的 INSERT 操作。如果用户在极端并发下重复插入,就会因为违反唯一索引而抛出异常。
配合使用 Redis + 唯一索引实现双重保障
- Redis:拦截大部分重复请求,提升性能
- 数据库唯一索引:兜底保障,防止极端并发场景中出现重复订单
相关文章:
秒杀系统解决两个核心问题的思路方法总结:1.库存超卖问题;2.用户重复抢购问题。
秒杀系统解决两个核心问题 秒杀系统解决两个核心问题:一、解决库存超卖的核心逻辑:解释:原子性保证: 二、如何避免重复抢购:使用 Redis 做唯一标识判断优点: 三、流程完整梳理:四、通过数据库建…...
linux socket编程之udp(实现客户端和服务端消息的发送和接收)
目录 一.创建socket套接字(服务器端) 二.bind将prot与端口号进行绑定(服务器端) 2.1填充sockaddr_in结构 2.2bind绑定端口 三.直接通信(服务器端) 3.1接收客户端发送的消息 3.2给客户端发送消息 四.客户端通信 4.1创建socket套接字 4.2客户端bind问题 4.3直接通信即可…...
SAP HANA使用命令行快速导出导入
楔子 今天折腾了接近一下午,就为了使用SAP HANA自带的命令行工具来导出数据备份。 SAP HANA(后续简称Hana)是内存数据库,性能这一方面上还真没怕过谁。 由于SAP HANA提供了Hana Studio这个桌面工具来方便运维和DBA使用…...
goland做验证码识别时报“undefined: gosseract.NewClient”
gosseract 应该有 和 c 相关的配置库因此需要安装 cgo 并且启用 CGO_ENABLED 在cmd下面输入这个 go env -w CGO_ENABLED1 接着输入 go env 验证是否设置成功 解决了这个问题后 “undefined: gosseract.NewClient” 又出现了 # runtime/cgo …...
计算机网络 实验四 静态路由的配置与应用
一、实验目的 掌握路由器基础工作原理及静态路由协议机制熟练使用华为ENSP网络模拟器进行拓扑设计与设备配置建立系统化的网络故障排除思维通过实践验证静态路由在中小型网络中的部署优势 二、实验环境 硬件配置:标准PC终端软件工具:华为企业网络模拟…...
Vue自定义指令-防抖节流
Vue2版本 // 防抖 // <el-button v-debounce"[reset,click,300]" ></el-button> // <el-button v-debounce"[reset]" ></el-button> Vue.directive(debounce, { inserted: function (el, binding) { let [fn, event "cl…...
[每周一更]-(第140期):sync.Pool 使用详解:性能优化的利器
文章目录 一、什么是 sync.Pool?二、sync.Pool 的基本作用三、sync.Pool 的主要方法四、sync.Pool 的内部工作原理五、sync.Pool 适用场景六、使用示例示例 1:基本使用输出示例:示例 2:并发使用 七、一个基于 sync.Pool 的 **Benc…...
3.QT-信号和槽|自定义槽函数|自定义信号}自定义的语法}带参数的信号和槽(C++)
信号和槽 Linux信号 Signal 系统内部的通知机制. 进程间通信的方式. 信号源:谁发的信号.信号的类型:哪种类别的信号信号的处理方式:注册信号处理函数,在信号被触发的时候自动调用执行. Qt中的信号和Linux中的信号,虽…...
健康养生之道
在快节奏的现代生活中,健康养生不再是中老年人的专属话题,越来越多的人开始意识到,合理的养生方式是保持良好身体状态和生活质量的关键。 饮食养生是健康的基石。遵循 “食物多样、谷类为主” 的原则,保证每天摄入足够的蔬菜、…...
Spark-SQL核心编程3
数据加载与保存 通用方式: SparkSQL 提供了通用的保存数据和数据加载的方式。这里的通用指的是使用相同的API,根据不同的参数读取和保存不同格式的数据,SparkSQL 默认读取和保存的文件格式为parquet 数据加载方法: spark.read.lo…...
TVM计算图分割--Collage
1 背景 为满足高效部署的需要,整合大量优化的tensor代数库和运行时做为后端成为必要之举。现在的深度学习后端可以分为两类:1)算子库(operator kernel libraries),为每个DL算子单独提供高效地低阶kernel实现。这些库一般也支持算…...
elementUI中MessageBox.confirm()默认不聚焦问题处理
在项目中使用elementUI的MessageBox.confirm()出现了默认不聚焦的问题,默认确认按钮是浅色的,需要点击一下才会变成正常。面对这种问题,创建新组件,实现聚焦。替换默认的MessageBox.confirm() 解决 创建components/MessageBoxCo…...
【刷题Day20】TCP和UDP(浅)
TCP 和 UDP 有什么区别? TCP提供了可靠、面向连接的传输,适用于需要数据完整性和顺序的场景。 UDP提供了更轻量、面向报文的传输,适用于实时性要求高的场景。 特性TCPUDP连接方式面向连接无连接可靠性提供可靠性,保证数据按顺序…...
sql server 预估索引大小
使用deepseek工具预估如下: 问题: 如果建立一个数据类型是datetime的索引,需要多大的空间? 回答: 如果建立一个数据类型是 datetime 的索引,索引的大小取决于以下因素: 索引键的大小&#…...
利用 i2c 快速从 Interface 生成 Class
利用 i2c 快速从 Interface 生成 Class(支持 TS & ArkTS) 在日常 TypeScript 或 ArkTS 开发中,需要根据 interface 定义手动实现对应的 class,这既重复又容易出错。分享一个命令行工具 —— interface2class,简称…...
MCGS昆仑通太屏笔记
4.3寸:4013ef/e1 7寸:7032kw 特点: 如果是使用组态屏进行调试使用,选择com1如果是实际项目使用,选择com2 操作步骤: 先创建设备窗口,再创建用户界面 在设备窗口界面,依次设置如下…...
服务治理-搭建Nacos注册中心
运行nacos.sql文件。 将准备好的nacos目录和nacos.tar包上传。 192.168.59.101是我的虚拟机ip,8848是我们设置的访问端口号。...
网络--socket编程(2)
Socket 编程 TCP TCP 网络程序 和刚才 UDP 类似 . 实现一个简单的英译汉的功能 TCP socket API 详解 下面介绍程序中用到的 socket API, 这些函数都在 sys/socket.h 中。 socket(): • socket() 打开一个网络通讯端口 , 如果成功的话 , 就像 open() 一样返回一个…...
【FreeRTOS进阶】优先级翻转现象详解及解决方案
【FreeRTOS进阶】优先级翻转现象详解及解决方案 接下来我们聊聊优先级翻转这个经典问题。这个问题在实时系统中经常出现,尤其是在任务较多的场景下,而且问题定位起来比较麻烦。 什么是优先级翻转? 优先级翻转的核心定义很简单:…...
结合建筑业务讲述TOGAF标准处理哪种架构
TOGAF标准处理哪种架构 内容介绍业务架构业务策略,治理,组织和关键业务流程数据架构组织的逻辑和物理数据资产以及数据管理资源的结构应用架构待部署的各个应用程序,它们之间的交互以及与组织核心业务流程的关系的蓝图技术架构支持业务&#…...
C++入门小馆: 深入string类(一)
嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的pa…...
NHANES指标推荐:WWI
文章题目:Weight-adjusted waist circumference index with hepatic steatosis and fibrosis in adult females: a cross-sectional, nationally representative study (NHANES 2017-2020) DOI:10.1186/s12876-025-03706-4 中文标题:体重调整…...
2025.04.18|【Map】地图绘图技巧全解
Add circles Add circles on a Leaflet map Change tile Several background tiles are offered by leaflet. Learn how to load them, and check the possibilities. 文章目录 Add circlesChange tile 2025.04.18【Map】| 地图绘图技巧全解1. 准备工作2. 地理区域着色图&…...
PR第一课
目录 1.新建 2.PR内部设置 3.导入素材 4.关于素材窗口 5.关于编辑窗口 6.序列的创建 7.视频、图片、音乐 7.1 带有透明通道的素材 8.导出作品 8.1 打开方法 8.2 导出时,需要修改的参数 1.新建 2.PR内部设置 随意点开 编辑->首选项 中的任意内容&a…...
C# 预定义类型全解析
在 C# 编程中,预定义类型是基础且重要的概念。下面我们来详细了解 C# 的预定义类型。 预定义类型概述 C# 提供了 16 种预定义类型,包含 13 种简单类型和 3 种非简单类型。所有预定义类型的名称都由全小写字母组成。 预定义简单类型 预定义简单类型表…...
@EnableAsync+@Async源码学习笔记之六
接上文,我们本文分析 AsyncExecutionAspectSupport 的源码: package org.springframework.aop.interceptor;import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFu…...
Java CMS和G1垃圾回收器
举个真带劲的例子:把JVM内存比作你家的祖传旱厕 想象你有个祖传旱厕,分三个坑: 新坑区(年轻代):刚拉的屎热乎着(新对象)陈年坑(老年代):风干的屎…...
Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
效果图: message.vue 消息组件 子组件 <template><div class"custom-notification"><div class"content"><span click"gotoMessageList(currentMessage.split()[1])">{{ currentMessage.split()[0] }}</…...
不规则曲面上两点距离求取
背景 在CT中求皮肤上两点间的弧长。由于人体表面并不是规则的曲面,不可能用圆的弧长求取方法来计算出两点间的弧长。 而在不规则的曲面上求两点的距离,都可以用类似测地线距离求取的方式来求取(积分),而转化为搜索路…...
Redis面试问题缓存相关详解
Redis面试问题缓存相关详解 一、缓存三兄弟(穿透、击穿、雪崩) 1. 穿透 问题描述: 缓存穿透是指查询一个数据库中不存在的数据,由于缓存不会保存这样的数据,每次都会穿透到数据库,导致数据库压力增大。例…...
