Java实现库存防超卖_高并发场景-订单库存防止超卖

本文介绍了在电商系统中如何利用Java实现库存防超卖,特别是在高并发场景下。通过使用Redis的分布式锁、 incr/decr 原子操作和SQL乐观锁来确保库存准确性,提高性能并避免阻塞用户。文中还探讨了不同库存扣减方案的优缺点,推荐使用Redis原子操作结合SQL乐观锁的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

在电商系统中买商品过程,先加入购物车,然后选中商品,点击结算,即会进入待支付状态,后续支付。

过程需要检验库存是否足够,保证库存不被超卖。

场景一:买家需要购买数量可以多件

场景二:秒杀活动,到时间点只能购买一件

目的

防止相同用户重复下单

检查库存准确数量

防止扣错库存数量

扣库存时性能效率提升、不阻塞用户

点赞再看,关注公众号:【地藏思维】给大家分享互联网场景设计与架构设计方案

掘金:地藏Kelvin https://juejin.im/user/5d67da8d6fb9a06aff5e85f7

主要解决手段

利用redis的incr、decr的原子性做操作

redis的lpush、rpop的原子性做操作,但是这个只能一个一个的扣,但不能原子地同时扣多个

sql乐观锁

交互流程

35b18dc0221ad86963d5835887c49f86.png

主要环节:购物车->结清->支付

本文讲述结清时,扣库存环节,分布式系统产生订单环节后续文章再详细分析。

备注:挺推荐使用https://www.processon.com/在线来做流程图的

一、防止重复

利用redis分布式锁

用分布式锁,是为了防刷、防止同一个用户同一秒里面把购物车里的商品进行多次结算,防止前端代码出问题触发两次。

利用Jedis客户端编写分布式锁

String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE

<think>我们正在讨论高并发场景防止库存的技术方案。根据引用内容,库存的主要原因包括:并发读取库存时数据不一致、库存扣减操作非原子性、缺乏有效的并发控制机制。解决方案可以从数据库层、缓存层以及分布式锁等方面考虑。###1.数据库层解决方案####1.1乐观锁在数据库更新时使用版本号(或时间戳)实现乐观锁。在更新库存时,检查版本号是否与读取时一致,并确保库存充足:```sqlUPDATEinventorySETstock=stock-#{quantity},version=version+1WHEREproduct_id=#{productId}ANDversion=#{version}ANDstock>=#{quantity}```如果更新影响的行数为0,则表示库存不足或版本号已变更(有其他请求已修改),此时应回滚事务或重试[^1]。####1.2悲观锁使用数据库的排他锁(如`SELECT...FORUPDATE`)在读取库存时锁定记录,确保后续更新操作在锁保护下进行。但这种方式在高并发下可能导致性能瓶颈。###2.缓存层解决方案(如Redis)####2.1原子操作利用Redis的原子操作(如`DECRBY`、`INCRBY`)来扣减库存:```redisDECRBYkey_namequantity```如果返回值大于等于0,则表示扣减成功;否则需要回滚(通过`INCRBY`恢复)[^2]。####2.2Lua脚本将库存检查和扣减操作封装在Lua脚本中,保证原子性:```lualocalstock=tonumber(redis.call('GET',KEYS[1]))ifstock>=tonumber(ARGV[1])thenredis.call('DECRBY',KEYS[1],ARGV[1])return1--成功elsereturn0--失败end```####2.3分布式锁使用Redis的`SETNX`(或Redisson的分布式锁)控制同一时间只有一个请求能执行库存扣减。但分布式锁在高并发下可能成为性能瓶颈。###3.库存分片对于大流量场景,可以将库存拆分为多个分片(如10个分片,每个分片持有部分库存)。用户请求随机路由到不同分片,减少单个分片的并发压力[^3]。但需注意:-库存分布在多个分片时,可能出现部分分片库存不足的情况,需设计补偿机制(如跨分片调拨)。-若用户一次购买多个商品,可能需要聚合多个分片的库存信息。###4.预扣库存订单创建前先预扣库存(如将库存状态标记为“预扣”),待支付成功后再实际扣减。若支付时,则释放预扣库存。这可以避免支付过程中的库存占用问题。###5.消息队列削峰将库存扣减请求放入消息队列(如Kafka、RabbitMQ),由消费者按顺序处理。通过控制消费者数量实现并发控制,但会引入异步延迟。###总结-**低并发场景**:数据库乐观锁即可满足。-**高并发场景**:推荐使用Redis原子操作或Lua脚本,配合库存分片和消息队列削峰。-**极端高并发**:可结合分片、预扣库存、异步队列等多种方案。§§相关问题§§1.乐观锁在高并发场景下重试次数过多怎么办?2.如何设计库存分片策略以减少跨分片调拨?3.Redis实现库存扣减时,如何保证缓存与数据库的数据一致性?4.预扣库存模式下,如何处理用户支付时后的库存释放?</think>### 高并发场景防止库存的解决方案 库存高并发场景中的常见问题,主要由**并发读取数据不一致**、**非原子性操作**和**缺乏并发控制**引起。以下是综合技术方案,结合数据库层、缓存层和架构优化: #### 一、数据库层解决方案 1. **乐观锁(版本号控制)** 在商品表中增加版本号字段,更新时校验版本号: ```sql UPDATE inventory SET stock = stock - #{buy_quantity}, version = version + 1 WHERE product_id = #{product_id} AND version = #{old_version} -- 读取时的版本号 AND stock >= #{buy_quantity} -- 防止负数库存 ``` - ✅ 优点:轻量级并发控制 - ❌ 缺点:高并发下失败率较高,需重试机制[^1] 2. **悲观锁(SELECT FOR UPDATE)** 事务中锁定记录: ```sql BEGIN; SELECT stock FROM inventory WHERE product_id = #{product_id} FOR UPDATE; -- 业务校验后更新库存 UPDATE inventory SET stock = stock - #{quantity} WHERE ...; COMMIT; ``` - ✅ 优点:保证强一致性 - ❌ 缺点:数据库性能瓶颈,死锁风险 #### 二、缓存层解决方案(Redis为核心) 1. **原子操作扣减库存** 使用Redis的原子命令: ```bash DECRBY stock_key {quantity} # 扣减库存 INCRBY stock_key {quantity} # 回滚库存 ``` - 执行后判断返回值:`>=0`表示成功,`<0`需回滚[^2] 2. **Lua脚本保证原子性** 将库存校验和扣减封装为原子操作: ```lua local stock = tonumber(redis.call('GET', KEYS[1])) if stock >= tonumber(ARGV[1]) then redis.call('DECRBY', KEYS[1], ARGV[1]) return 1 -- 成功 end return 0 -- 失败 ``` 3. **分布式锁控制** 使用Redisson实现分布式锁: ```java RLock lock = redisson.getLock("stock_lock"); lock.lock(); try { // 执行库存操作 } finally { lock.unlock(); } ``` #### 三、架构层优化 1. **库存分片** - 将总库存拆分为多个子库存(如`stock_1`、`stock_2`) - 请求通过哈希路由到不同分片,降低单点压力 - ⚠️ 注意:需处理跨分片库存调拨问题[^3] 2. **异步队列削峰** ```mermaid graph LR 用户请求-->消息队列(Kafka/RabbitMQ) 消息队列-->消费者组[库存服务集群] 消费者组-->数据库 ``` - 请求先入队列,消费者按可控速度处理 3. **预扣库存机制** - 步骤1:用户下单时预占库存(状态为"锁定") - 步骤2:支付成功后实际扣减 - 步骤3:时未支付自动释放库存 #### 四、方案选型建议 | 场景 | 推荐方案 | 适用并发量 | |------------|------------------------------|------------| | 低频抢购 | 数据库乐观锁 | < 1000 QPS | | 秒杀场景 | Redis原子操作 + Lua脚本 | 1万~10万 QPS | | 极端高并发 | 库存分片 + 异步队列 | > 10万 QPS | > **关键注意事项**: > 1. Redis与数据库需通过**双写机制**或**定时同步**保证数据一致性 > 2. 必须实现**库存回滚逻辑**(如订单取消时) > 3. 前端添加**限流策略**(如令牌桶算法) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值