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

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

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

  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;  }  }  }  
}

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • redis链表结构和简单动态字符串(SDS)
  • Docker之镜像与容器的相关操作
  • docker-相关
  • STL--list和vector有什么区别
  • 企业如何选择合适自己的ERP系统?ERP系统应该具有哪些功能和特点?
  • 让智能体像孩子一样观察别人学习动作,跨视角技能学习数据集EgoExoLearn来了
  • 前端(动态雪景背景+动态蝴蝶)
  • VScode使用Prettier格式化代码
  • Mysql的基本命令
  • C语言分支语句
  • 19.删除链表的倒数第N个节点
  • 国内:深圳交通流量数据集
  • PurpleKeep:提供Azure管道以创建基础设施并执行Atomic测试
  • 流行的API架构学习
  • Hive安装配置
  • Akka系列(七):Actor持久化之Akka persistence
  • JavaScript创建对象的四种方式
  • js对象的深浅拷贝
  • learning koa2.x
  • PV统计优化设计
  • spring-boot List转Page
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • 诡异!React stopPropagation失灵
  • 机器学习学习笔记一
  • 京东美团研发面经
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 移动端 h5开发相关内容总结(三)
  • 用jQuery怎么做到前后端分离
  • 在electron中实现跨域请求,无需更改服务器端设置
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • elasticsearch-head插件安装
  • 带你开发类似Pokemon Go的AR游戏
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​批处理文件中的errorlevel用法
  • ​虚拟化系列介绍(十)
  • #pragma once与条件编译
  • #Z0458. 树的中心2
  • (1)常见O(n^2)排序算法解析
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (SpringBoot)第二章:Spring创建和使用
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (补充)IDEA项目结构
  • (南京观海微电子)——I3C协议介绍
  • (强烈推荐)移动端音视频从零到上手(下)
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (算法)硬币问题
  • (五)Python 垃圾回收机制
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (转)菜鸟学数据库(三)——存储过程
  • (转)视频码率,帧率和分辨率的联系与区别
  • .net core 的缓存方案
  • .net framework 4.8 开发windows系统服务
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • .NET 反射 Reflect
  • .NET 事件模型教程(二)