秒杀活动库存扣减逻辑详解:从批量到单个,再到缓存与日志记录

场景是在进行秒杀活动时处理库存扣减的逻辑。下面我会提供一个简化的处理流程,并解释每一步的操作。

  1. 批量扣减库存:

    • 当用户发起秒杀请求时,系统首先尝试批量扣减库存。
    • 这通常涉及到从数据库(如MySQL)中读取当前库存数量,然后减去请求的数量。
    • 如果批量扣减成功,则继续处理订单等其他逻辑。
  2. 批量扣减失败:

    • 如果由于某种原因(如库存不足)批量扣减失败,则改为单个扣减库存。
    • 这意味着系统尝试逐一扣减每个商品的库存,直到库存耗尽或满足用户请求的数量。
  3. 单个扣减库存:

    • 在单个扣减的过程中,系统仍然需要确保并发安全性,避免超卖。
    • 这通常通过数据库的事务、锁或其他并发控制机制来实现。
  4. 扣减完库存后写入Redis:

    • 一旦库存扣减成功(无论是批量还是单个),系统需要将库存数量更新到Redis中。
    • Redis作为一个高速缓存,可以提供更快的库存查询速度,减轻数据库压力。
    • 在Redis中设置库存为0表示该商品已售罄。
  5. 写入秒杀失败表到MongoDB:

    • 对于那些因为库存不足而未能成功秒杀的用户,系统需要将他们的请求信息写入到MongoDB的秒杀失败表中。
    • 这有助于后续的分析和补偿措施,例如通知用户下次秒杀活动的时间或提供其他优惠。
    • MongoDB作为一个面向文档的数据库,适合存储这种结构化的日志或事件数据。

注意事项:

  • 并发控制: 在高并发的秒杀场景下,确保库存扣减的原子性和准确性是关键。需要使用数据库的事务、锁或其他机制来确保这一点。
  • 性能优化: Redis和MongoDB的引入可以提高系统的响应速度和可扩展性。但也需要确保这些组件的性能和稳定性,避免成为新的瓶颈。
  • 异常处理: 在整个流程中,需要妥善处理各种异常情况,如网络中断、数据库故障等,确保系统的健壮性。
  • 日志记录: 对于关键操作,如库存扣减、写入Redis和MongoDB等,需要记录详细的日志,以便后续的问题追踪和分析。
import com.mongodb.client.MongoCollection;  
import com.mongodb.client.MongoDatabase;  
import org.bson.Document;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.data.redis.core.RedisTemplate;  
import org.springframework.data.redis.core.ValueOperations;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Transactional;  import javax.annotation.Resource;  
import java.util.concurrent.TimeUnit;  @Service  
public class SeckillService {  @Autowired  private ProductRepository productRepository; // 假设的库存数据访问层  @Resource  private RedisTemplate<String, Long> redisTemplate; // Redis操作模板  @Autowired  private MongoDatabase mongoDatabase; // MongoDB数据库实例  // 批量扣减库存  @Transactional  public boolean tryBatchDecreaseStock(String productId, int quantity) {  // 读取数据库中的库存数量  long stock = productRepository.getStock(productId);  if (stock >= quantity) {  // 批量扣减库存  long newStock = stock - quantity;  if (productRepository.updateStock(productId, newStock)) {  // 批量扣减成功,更新Redis中的库存数量  updateRedisStock(productId, newStock);  return true;  }  }  return false;  }  // 单个扣减库存  @Transactional  public boolean trySingleDecreaseStock(String productId, int quantity) {  for (int i = 0; i < quantity; i++) {  // 读取数据库中的库存数量  long stock = productRepository.getStock(productId);  if (stock > 0) {  // 单个扣减库存  long newStock = stock - 1;  if (productRepository.updateStock(productId, newStock)) {  // 单个扣减成功,更新Redis中的库存数量  updateRedisStock(productId, newStock);  } else {  // 更新失败,可能由于并发导致的数据不一致,返回失败  return false;  }  } else {  // 库存不足,返回失败  break;  }  }  return true;  }  // 更新Redis中的库存数量  private void updateRedisStock(String productId, long stock) {  ValueOperations<String, Long> ops = redisTemplate.opsForValue();  ops.set(productId, stock, 1, TimeUnit.MINUTES); // 设置库存数量,并设置过期时间  }  // 写入秒杀失败记录到MongoDB  public void recordSeckillFailure(String userId, String productId) {  MongoCollection<Document> collection = mongoDatabase.getCollection("seckill_failures");  Document doc = new Document("user_id", userId)  .append("product_id", productId)  .append("failure_time", new java.util.Date());  collection.insertOne(doc);  }  // 实际业务逻辑中调用这些方法  public boolean processSeckillOrder(String userId, String productId, int quantity) {  if (tryBatchDecreaseStock(productId, quantity)) {  // 批量扣减成功,处理订单等其他逻辑...  return true;  } else {  // 批量扣减失败,尝试单个扣减  if (trySingleDecreaseStock(productId, quantity)) {  // 单个扣减成功,处理订单等其他逻辑...  return true;  } else {  // 扣减库存失败,记录秒杀失败  recordSeckillFailure(userId, productId);  return false;  }  }  }  
}

 

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

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

相关文章

skynet sproto编译

一、 git下载sproto https://github.com/cloudwu/sproto 二、编译 1、 去到 sproto 目录&#xff0c; 执行 make 如果出现如下报错 lsproto.c:881:12: 错误&#xff1a;expected ‘’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘int’LUAMOD_API int 打开&#x…

DBU-Net:用于乳腺超声图像中肿瘤分割的双分支U形网络

DBU-Net&#xff1a;用于乳腺超声图像中肿瘤分割的双分支U形网络 摘要引言材料和方法概述所提出的方法 DBU-Net Dual branch U-Net for tumor segmentation in breast ultrasound images 摘要 乳腺超声医学图像通常具有低成像质量沿着不清楚的目标边界。这些问题使得医生在诊断…

从汇编看函数调用

文章目录 函数调用流程栈相关寄存器及的作用简介寄存器功能指令功能 栈函数的括号{}正括号反括号 参数传递传值&#xff0c;变量不可改传指针&#xff0c;变量可改C 传引用 函数调用实例 函数调用流程 目标&#xff1a;函数调用前后栈保持不变 保存main函数的寄存器上下文移…

导入excel内容

摘要&#xff1a; MultipartFile工具类&#xff1a; MultipartFile是SpringMVC提供简化上传操作的工具类。在不使用框架之前&#xff0c;都是使用原生的HttpServletRequest来接收上传的数据&#xff0c;文件是以二进制流传递到后端的&#xff0c;然后需要我们自己转换为File类&…

Android音视频开发 - MediaMetadataRetriever 相关

Android音视频开发 - MediaMetadataRetriever 相关 MediaMetadataRetriever 是android中用于从媒体文件中提取元数据新的类. 可以获取音频,视频和图像文件的各种信息,如时长,标题,封面等. 1:初始化对象 private MediaMetadataRetriever mediaMetadataRetriever new MediaMe…

Regression算法

文章目录 用线性回归找到最佳拟合直线标准回归函数局部加权线性回归函数 用线性回归找到最佳拟合直线 from google.colab import drive drive.mount("/content/drive")Mounted at /content/drivefrom numpy import *def loadDataSet(fileName):numFeat len(open(fi…

并发线程基础第八篇

目录 线程池 自定义线程池 步骤1&#xff1a;自定义拒绝策略接口 步骤2&#xff1a;自定义任务队列 步骤3&#xff1a;自定义线程池 步 骤 4&#xff1a;测 试 ThreadPoolExecutor 线程池状态 构造方法 工作方式 newFixedThreadPool newCachedThreadPool newSingleTh…

前端学习<四>JavaScript基础——02-JavaScript入门:hello world

开始写第一行 JavaScript&#xff1a;hello world JS 代码的书写位置在哪里呢&#xff1f;这个问题&#xff0c;也可以理解成&#xff1a;引入 JS 代码&#xff0c;有哪几种方式&#xff1f;有三种方式&#xff1a;&#xff08;和 CSS 的引入方式类似&#xff09; 行内式&…

C#开发中获取XML节点值,XML转对象案例

一、获取XML节点值 string strMsg"XML内容";XmlDocument xmlDoc new XmlDocument(); xmlDoc.LoadXml(strMsg);// 创建命名空间管理器 XmlNamespaceManager nsManager new XmlNamespaceManager(xmlDoc.NameTable); nsManager.AddNamespace("soapenv", &q…

我与C++的爱恋:内联函数,auto

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 ​ 一、内联函数 1.内联函数的概念 内联函数目的是减少函数调用的开销&#xff0c;通过将每个调用点将函数展开来实现。这种方法仅适用于那些函数体小、调用频繁的函数。 …

redis事务(redis features)

redis支持事务&#xff0c;也就是可以在一次请求中执行多个命令。redis中的事务主要是通过MULTI和EXEC这两个命令来实现的。 MULTI命令用来开启一个事务&#xff0c;事务开启之后&#xff0c;所有的命令就都会被放入到一个队列中&#xff0c;最后通过一个EXEC命令来执行事务中…

基于java+SpringBoot+Vue的网上订餐系统设计与实现

基于javaSpringBootVue的网上订餐系统设计与实现 开发语言: Java 数据库: MySQL技术: Spring Boot JSP工具: IDEA/Eclipse、Navicat、Maven 系统展示 前台展示 菜品浏览与选择&#xff1a;用户可以浏览不同的菜品分类&#xff0c;并选择心仪的菜品。 订单创建与管理&…

任意文件下载漏洞

1.文件下载漏洞存在的位置 文件经过php处理可能存在文件下载漏洞&#xff0c;配合目录遍历漏洞使用 2.目录遍历漏洞检验方法 测试是否存在目录遍历漏洞&#xff1a;在网站网址中间添加随意写一个文件名../&#xff08;返回上一级&#xff09;进行测试&#xff0c;没有报错就…

备战蓝桥杯--数论与搜索刷题2

话不多说&#xff0c;直接看题&#xff1a; 1.辗转相减法 我们不妨假设原等比数列a,a*(q/p),a*(q/p)^2.... 那么x1,,,,xn就是其中的n项&#xff0c;xi/x1(q/p)^b&#xff0c;假设最大比例为(q/p)^k&#xff0c;&#xff0c;那么一定有(q/p)^(k*s)(q/p)^b&#xff0c;即k是b的…

【Servlet】Servlet入门

文章目录 一、介绍二、入门案例导入servlet-api的解决办法 一、介绍 概念&#xff1a;server applet&#xff0c;即&#xff1a;运行在服务器端的小程序 Servlet就是一个接口&#xff0c;定义了Java类被浏览器访问到&#xff08;tomcat识别&#xff09;的规则。 将来我们定义…

【项目新功能开发篇】开发编码

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

mysql 磁盘空间100%

MySQL大事务可能会导致过多的占用临时文件&#xff0c;导致磁盘空间撑满的问题 本例说明下binlog cache产生的临时文件 案例复现 调小binlog_cache_size&#xff0c;让DML使用临时文件 使用存储过程模拟大事务 创建表 create table t1( id int AUTO_INCREMENT, name varchar…

Rust---复合数据类型之字符串与切片(2)

目录 字符串操作删除 (Delete)连接 (Concatenate) 字符串转义 前情回顾: Rust—复合数据类型之字符串&#xff08;1&#xff09; 字符串操作 删除 (Delete) 删除方法仅适用于 String 类型&#xff0c;分别是&#xff1a; pop()&#xff0c;remove()&#xff0c;truncate()&a…

【Redis系列】Redis安装与使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

使用subprocess包来在python代码实时查看GPU利用率

最近又被GPU利用率问题导致训练不高效的问题搞到了&#xff08;恼&#xff09;&#xff0c;所以在py使用代码看看是哪出了问题。 import subprocessdef get_gpu_utilization():# 运行nvidia-smi命令smi_output subprocess.check_output([nvidia-smi, --query-gpuutilization.…