「java扣减库存防止超卖」java解决超卖问题

博主:adminadmin 2023-01-27 19:57:10 366

今天给各位分享java扣减库存防止超卖的知识,其中也会对java解决超卖问题进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

解决商品超卖问题

一般的商品预下单会有一下流程,判断商品的库存,如果有库存,那么减库存,生成订单(这两个操作是一个事务)。在高并发的情况下,会有超卖的情况下。

问题产生的原因是:多个线程(请求)同时进入减库存的操作,判断商品库存的方法尽管有,但是判断通过了,解决方法有多个:

1.分布式情况下,采用分布式锁,可以用zookeeper+curator实现,在判断商品库存的方法前,先去申请锁

2.在更新库存的表中添加判断,and stock_count 0(这里简化场景,秒杀业务一次只允许秒杀一个商品),如果一个用户只能秒杀一次商品,需要在关联表中,对用户id和商品id建立唯一索引。

Java如何解决超卖

一、人数阀门设计:进行用户人群过滤。

商品数量只有100份,秒杀人数有10000人,那么我们就设计1道阀门(根据情况,可以设计3道或者2道都可以的)。

在整点的时候,我们对点击了“购买”按钮后,我们只运行500人进入信息填写页面,信息填写完成后提交订单。效果如下:

①商品详情点击购买(秒杀)--》②输入信息提交订单--》③进行支付

10000人 500人 (这里也可以设计阀门,只允许多少人进入支付)

其他未进入的如何处理乃?显示已抢完或者排队等待(这就是后面要提到的排队系统设计)。

二、会员排队设计:对用户进行排队,排在前面的先购买

这相当于是消息队列模式了,如果秒杀是立即知道结果,排队可能会有点鸡肋。

在第二步②输入信息提交订单后进行排队,排在前面的先购买,排在后面的后购买

三、问答问题设计:过滤掉一些反应慢的用户

在第一步①点击购买后跳转到问题页面,用户必须回答正确问题后,方可进入后面的流程

四、库存缓存设计:缓存库存,判断用户购买的商品是否还有,不读取数据库,速度快,也不会增加数据库负担,

经过前面的过滤,超卖的可能性比较低了提前将商品库存缓存起来,到下单购买的时候,用户购买了就减1,每次都通过库存缓存判断一下,如果为0就显示已抢完。

五、页面静态设计:尽量静态缓存化【CDN那些这里不做考虑】

第一步①商品详情页面,尽量进行缓存,减轻大批量用户在访问商品页面的时候,大量查询数据库。

问答问题页面:全静态,加载快,无数据库负担。

排队等待页面:全静态,加载快,无数据库负担。

排队结束页面:全静态,加载快,无数据库负担。

利用Redis设计库存系统的苦与乐

在秒杀等高并发场景下,既要保证库存安全,也要拥有极高的系统性能。从存储结构上,很多同学会选用Redis,毕竟Redis的单线程操作特性,很好地避免了线程安全的问题,同时具备极高的读写性能。

我们先来看下库存系统设计的几大核心要点:

1. 库存安全:既要保证线程安全,也要防止出现超卖

2. 同步响应:业务场景基本不允许异步响应库存扣减结果

3. 性能极限:在seckill场景下,性能总是被要求越高越好

我们来看下如何利用Redis来解决上面的三个问题。

一.库存安全

利用Redis来做库存扣减,避免超限的"方法"很多,坑也很多,我们先来看下常用的陷阱有哪些。

1. 先获取当前库存值进行比较,再进行扣减

defdecr_stock():conn=redis_conn()key="productA"current_storage=conn.get(key)current_storage_int=int(current_storage)ifcurrent_storage_int=0 :return0result=conn.decr(key)returnresult

我们先在Redis中拿到当前的库存值,然后check是否已经扣减到了零,如果已经扣减到了零,则直接return;否则,就利用Redis的decr原子操作进行扣减,同时返回扣减后的库存值。

这种方法的问题很明显,在并发条件下,会出现脏读,设想一个场景,AB两个请求进来,A获取的库存值为1,B获取的库存值为1,然后两个请求都被发到redis中进行扣减操作,然后这种场景下,A最后得到的库存值为0;但是B最后得到的库存值为-1,超限。

2. 先扣减库存,再做比较,跟进情况是否做回滚

defdecr_stock():conn=redis_conn()key="productA"current=conn.decr(key)ifcurrent=0:returncurrentelse:          #回滚库存conn.incr(key)return0

直接先对库存值进行扣减,得到当前的库存值;然后,对此库存值进行check,如果库存=0,则返回库存值,如果库存0,则回滚库存,以便于防止负库存量的存在。

Redis Decr命令:DECR 命令会返回键 key 在执行减1操作之后的值。

这种做法引入了两个新的问题:

1).如果大批量的并发请求过来,redis承受的写操作的量,是加倍的,因为回滚库存的存在导致的。所以这种情况下,高并发量进来,极有可能将redis的写操作打出极限值,然后会出现很多redis写失败的错误警告

2). Redis的Decr操作和回滚操作无法保证原子性,在宕机情况下,容易产生数据不一致

3.先扣库存,然后通过整数溢出控制,根据情况进行回滚

defdecr_stock():conn=redis_conn()key="productA"current=conn.decr(key)      #通过整数控制溢出的做法ifcheck_overflow(current):returncurrentelse:          #回滚库存conn.incr(key)return0  defcheck_overflow(stock):      #如果当前库存未被递减到0,则check_number为int类型,isinstance方法检测结果为true      #如果当前库存已被递减到负数,则check_number为long类型,isinstance方法检测结果为falsecheck_number=sys.maxint - stockcheck_result=isinstance(check_number,int)returncheck_result

这种做法和方法2类似,只是比对部分由直接和0比对,变成了通过检测integer是否溢出的方式来进行。这样就彻底解决了高并发情况下,直接和零比对,限制不住的问题了。

虽然此种做法,相对于做法二说来,要靠谱很多,但是仍然解决不了在高并发情况下,redis写并发量加倍的问题,极有可能某个促销活动,在开始的那一刻,直接将redis的写操作打出问题来。

4.基于分布式锁的库存扣减

defdecr_stock():key ="productA"    lock = getLock(key)iflocked ==1:        current_storage = conn.get(key)        current_storage_int = int(current_storage)ifcurrent_storage_int=0:return0        result = conn.decr(key)returnresultelse:return"someone in it"

Redis在2.8以后支持Lua脚本的原子性操作,可以用来做分布式锁,解决超限的问题。

5. All in Lua

defstorage_scenario_six():        conn = redis_conn()lua ="""                local storage = redis.call('get','storage_seckill')                if  storage ~= false then                    if tonumber(storage) 0 then                        return redis.call('decr','storage_seckill')                    else                        return 'storage is zero now, can't perform decr action'                    end                else                    return redis.call('set','storage_seckill',10)                end              """result = conn.eval(lua,0)        print(result)

二、同步响应

如果只用Redis来进行存储,处理完数据直接返回前端即可。如果还要持久化到DB,要尽量避免直接操作DB,因为DB往往是最大的IO瓶颈,如果要异步落库到DB,比如使用MQ。要注意处理Redis扣减和消息发送的原子性处理。

三、性能

官网上redis的读写性能能到10W/QPS左右,这个量级应该可以解决绝大部分的场景。

但是经常有同学在压测的时候达不到这个性能,主要还是卡在网络环境上,在5W/QPS的时候,带宽就超过10M/s了。所有想追求Redis的极致性能,最好还是在同机房进行调用。

如何解决高并发秒杀的超卖问题

由秒杀引发的一个问题

我们假设现在商品只剩下一件了,此时数据库中 num = 1;

但有100个线程同时读取到了这个消息 num = 1 ,所以100个线程都开始减库存了。

但你最终会发觉, 其实只有一个线程减库存成功,其他99个线程全部失败。

为何?

这就是MySQL中的排他锁起了作用。

排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存, 如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁 ,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

就是类似于我在执行update操作的时候,这一行是一个事务 (默认加了排他锁 )。 这一行不能被任何其他线程修改和读写

这种方式采用了 版本号 的方式,其实也就是 CAS 的原理。

假设此时version = 100, num = 1; 100个线程进入到了这里,同时他们select出来版本号都是version = 100。

然后直接update的时候,只有其中一个先update了,同时更新了版本号。

那么其他99个在更新的时候,会发觉version并不等于上次select的version,就说明version被其他线程修改过了。那么我就放弃这次update

利用redis的单线程预减库存。比如商品有100件。那么我在redis存储一个k,v。例如

每一个用户线程进来,key值就减1,等减到0的时候,全部拒绝剩下的请求。

那么也就是说只有100个线程会进入到后续操作。所以一定不会出现超卖的现象

可见第二种CAS是失败重试,并无加锁。应该比第一种加锁效率要高很多。 类似于Java中的Synchronize和CAS 。

电商,减库存,如何保证不超卖

设置拍下减库存!保证不超卖!但是防止不了乱拍多拍!一般多拍乱拍后台到了时间订单会关闭库存又可以自动填上的!比如只剩一个库存!有人拍下了未付款!第二个买家拍不了!要等第一个买家关闭了订单才可以!

java 和mysql想实现以下库存减少的功能,别人现在买去2个货,我要减去库存。怎么实现最方便呢。

可以直接完成-2,语句如下:

update druginfo set stockNum=stockNum-2 where drugId=123

但是一般可能需要先查出数来,要判断够不够减,不能减成负数。

关于java扣减库存防止超卖和java解决超卖问题的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。