以下是个简单的库存扣减流程:
image.png
如果并发非常低的时候,基本就按这个流程走就行了。
而这个设计,并发量稍大时,就会导致超卖的情况出现,两个同时要9台手机的请求,同时查到库存有12台,那操作下来,就会导致超卖:
image.png
嗯,那么出于职业道德,得加把锁:
image.png
嗯,这个设计,在高并发但库存量极少的秒杀场景,或者库存很高但并发量不高的(每秒10个请求),都是可行解。
然而,这个相当于把程序串行化了。那么假设处理一个订单要200毫秒,在库存量很高(10万台),并发量极高(每秒1000个请求),有个请求就要等待200秒了,这个肯定不能接受的。
解决办法:
1.库存分段
将10万库存,分成100段,每段1000个库存。对应的,就有100把锁去锁这100个库存段了,可以满足100个线程同时跑。
image.png
这套方案确实可以解决高并发,高库存问题,然而库存分段也是个麻烦的事。
我这里还有个方案,虽然效率略低,但是跑起来应该还好。
2.库存占用
image.png
把串行化的步骤,改成了只是简单地往“库存占用表”插入数据即可,耗费的时间是低的,可以应付较高的并发量。
实现方式:
public static void main(String[] args) {
try{
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6222");
config.useSingleServer().setPassword("test");
RedissonClient finalRedisson = Redisson.create(config);
//需求数量
int requireQty = 9;
for(int i=0;i<30;i++){
Thread t = new Thread(()->{
try{
RLock rLock = finalRedisson.getLock("myLock");
System.out.println(Thread.currentThread().getName()+ "开始");
RAtomicLong stockQty = finalRedisson.getAtomicLong("stockQty");
RAtomicLong stockOccupy = finalRedisson.getAtomicLong("stockOccupy");
rLock.lock();
long l1 = stockQty.get();
long l2 = stockOccupy.get();
long l = l1 - l2;
System.out.println(Thread.currentThread().getName() + "获得锁");
System.out.println(Thread.currentThread().getName() + "do something");
if(l >= requireQty) {
stockOccupy.set(stockOccupy.get()+requireQty);
}
rLock.unlock();
//创建订单,扣减库存
Thread.sleep(200);
if(l >= requireQty) {
System.out.println(Thread.currentThread().getName() + "done,库存剩下:" + l);
}
else {
System.out.println(Thread.currentThread().getName() + "库存不足,库存剩下:" + l);
}
// System.out.println(Thread.currentThread().getName() + "准备释放锁");
System.out.println(Thread.currentThread().getName() + "结束");
}
catch (Exception ex){
System.out.println(ex.getMessage());
}
});
t.start();
}
}
catch (Exception ex){
System.out.println(ex.getMessage());
}
finally {
}
}
执行前:
image.png
image.png
执行后:
image.png
部分执行结果:
image.png
剩下1个库存的时候,就跑步下去了。
网友评论