如何设计一个秒杀系统

设计一个秒杀系统时,Redis可以作为一个高性能的工具来防止库存超卖。以下是具体的设计思路和实现方法:

#牛客AI配图神器#

1. 使用 Redis 的原子操作

Redis 提供了多种原子性操作命令,这些命令可以在高并发场景下确保数据的一致性。

  • INCR/DECR:通过递增或递减某个键的值来控制库存。
  • GETSET:获取当前值并设置新值,保证操作的原子性。
  • Lua 脚本:将多个操作封装在一个 Lua 脚本中执行,Redis 会保证脚本内的操作是原子性的。

示例代码(使用 Lua 脚本)

local stock_key = KEYS[1]
local stock_count = tonumber(redis.call('get', stock_key))

if stock_count > 0 then
    redis.call('decr', stock_key)
    return 1  -- 表示扣减成功
else
    return 0  -- 表示库存不足
end

2. 分布式锁

在高并发场景下,可以通过分布式锁来确保同一时间只有一个请求能够修改库存。

  • SETNX:尝试设置一个键,如果键不存在则设置成功,否则失败。
  • Redlock 算法:更复杂的分布式锁实现,适合多 Redis 实例环境。

示例代码(使用 SETNX)

String lockKey = "lock:product:" + productId;
boolean locked = redis.setnx(lockKey, "1");

if (locked) {
    try {
        // 扣减库存逻辑
        redis.decr("stock:" + productId);
        return "秒杀成功";
    } finally {
        // 释放锁
        redis.del(lockKey);
    }
} else {
    return "秒杀失败,库存不足或正在处理其他请求";
}

3. 预减库存

在用户下单前,先进行库存预减操作,避免用户提交订单时库存已不足。

  • 在用户加入购物车或点击秒杀按钮时,立即扣减 Redis 中的库存。
  • 如果后续订单支付失败或超时未支付,再将库存归还。

示例代码(预减库存)

String stockKey = "stock:" + productId;

// 尝试扣减库存
Long stock = redis.decr(stockKey);

if (stock >= 0) {
    return "秒杀成功,等待支付";
} else {
    // 库存不足时回滚
    redis.incr(stockKey);
    return "秒杀失败,库存不足";
}

4. 限流与队列

为了进一步保护系统,可以引入限流和消息队列机制。

  • 限流:通过令牌桶或漏桶算法限制请求频率。
  • 消息队列:将秒杀请求放入队列中异步处理,减少对数据库的压力。

示例代码(使用 Redis 实现限流)

String rateLimitKey = "rate:limit:" + userId;

// 每秒允许 5 次请求
long allowedRequests = 5;
long timestamp = System.currentTimeMillis();

if (redis.exists(rateLimitKey)) {
    long lastRequestTime = Long.parseLong(redis.get(rateLimitKey));
    if (timestamp - lastRequestTime < 1000 / allowedRequests) {
        return "请求过于频繁,请稍后再试";
    }
}

// 更新最后请求时间
redis.set(rateLimitKey, String.valueOf(timestamp), 1, TimeUnit.SECONDS);
return "请求通过";

总结

通过以上方法,Redis 可以有效防止库存超卖:

  • 使用原子操作确保库存扣减的一致性。
  • 引入分布式锁避免并发冲突。
  • 实现预减库存机制提前锁定商品。
  • 结合限流和消息队列优化系统性能。

这些技术结合使用,可以构建一个高效、稳定的秒杀系统。

#设计人的面试记录##找工作时的取与舍##牛客创作赏金赛#
职保镖-扶你上马 文章被收录于专栏

知识分享,交天下朋友,扶你上马,送你一层,职业规划,面试指导、高薪谈判、背调辅助

全部评论

相关推荐

12-14 14:24
已编辑
门头沟学院 Java
八股很久没复习,回答的一坨大学期间最得意的项目是什么?为什么要做这个电商商城项目?这个电商商城项目里你是自己独立完成还是合作完成的?最近一年你印象最深刻的开发卡点是什么?Java&nbsp;中线程控制的方法有哪些呢?AOP(面向切面编程)是什么?过滤器和拦截器有什么区别?注解的原理可以简单讲一下&nbsp;@Autowired&nbsp;注解吗?它和&nbsp;@Resource&nbsp;的区别是什么?你熟练&nbsp;MySQL&nbsp;数据库索引,索引的分类可以讲一下吗?怎么创建一个&nbsp;MySQL&nbsp;索引?哪些情况会导致&nbsp;MySQL&nbsp;索引失效?事务的四个特性和四个隔离级别分别是什么?使用&nbsp;Redis&nbsp;做缓存会存在什么问题?秒杀系统中如何保证缓存和数据库的一致性?项目中用到的登录相关技术(Cookie、Session、SSO、JWT&nbsp;等),可以解释并说明它们之间的区别吗?可以简单介绍几种常用的数据结构及其特点吗?对于难以复现、几率极低的&nbsp;bug,你会按哪些步骤处理?你更喜欢接手已有项目还是做全新的项目?为什么?如果设计一个新项目,应该优先考虑什么?新加入团队,你觉得自己该怎么做才能更快融入团队、进入工作状态?最近有关注什么技术热点吗?你使用过或了解过哪些数据库?可以简述&nbsp;MySQL、Redis、PG&nbsp;的区别吗?手撕:全排列空闲时间一般会做什么?------------------------------已过,hr面没接
查看25道真题和解析
点赞 评论 收藏
分享
12-04 04:20
门头沟学院 Java
1.&nbsp;简单介绍项目背景、开发流程及系统核心功能。2.&nbsp;分布式场景下如何解决用户连续两次提交的问题?3.&nbsp;仅开发后端接口吗?是否涉及前端工作?4.&nbsp;后端部分是从0到1独立开发,还是基于现有项目二次修改?5.&nbsp;请讲解JWT在项目中的具体执行流程。6.&nbsp;未搭建前端但登录功能需前后端紧密配合,对此是如何处理的?7.&nbsp;JWT与OAuth&nbsp;2.0之间是什么关系?8.&nbsp;请说明JWT与OAuth&nbsp;2.0的对比过程及结论。9.&nbsp;项目中生成的Token除用户名和密码外,还包含哪些信息?10.&nbsp;调研过业界登录功能的其他实现方式吗?除了JWT之外还有哪些?11.&nbsp;项目中ES存储的题库内容总量有多少?12.&nbsp;请解释什么是倒排索引。13.&nbsp;ES中的数据是如何导入的?是否结合了数据库使用?14.&nbsp;该场景下直接使用事务是否可行?为什么?15.&nbsp;使用RocketMQ时,是否调研过其他开源消息队列?对比结果如何?16.&nbsp;消息队列中如何处理消息的幂等消费问题?17.&nbsp;了解类加载的完整过程吗?请简要说明。18.&nbsp;类初始化时,静态代码块、常量、复合函数的执行顺序是什么?(结合代码判断输出结果)19.&nbsp;请讲解JVM的内存结构。20.&nbsp;常见的垃圾回收器有哪些?21.&nbsp;G1相对于CMS有哪些核心提升?22.&nbsp;如何认定“价值比较大”?(结合上下文场景说明)23.&nbsp;了解JVM的相关参数吗?请举例说明。24.&nbsp;项目中目前使用的是什么垃圾回收器?25.&nbsp;Spring中注入Bean有几种方式?26.&nbsp;请讲解Spring中AOP的原理及应用场景。27.&nbsp;场景题:设计美团话费充值功能的MySQL表结构,需要设计几张表才能实现核心功能?28.&nbsp;针对充值记录表,编写对应的建表语句。
查看28道真题和解析
点赞 评论 收藏
分享
评论
3
29
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务