分布式锁和mysql事物扣库存_高并发下库存扣减解决方案--分布式锁

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:11   2077   0

以下是个简单的库存扣减流程:

9a04aaaf6e87

image.png

如果并发非常低的时候,基本就按这个流程走就行了。

而这个设计,并发量稍大时,就会导致超卖的情况出现,两个同时要9台手机的请求,同时查到库存有12台,那操作下来,就会导致超卖:

9a04aaaf6e87

image.png

嗯,那么出于职业道德,得加把锁:

9a04aaaf6e87

image.png

嗯,这个设计,在高并发但库存量极少的秒杀场景,或者库存很高但并发量不高的(每秒10个请求),都是可行解。

然而,这个相当于把程序串行化了。那么假设处理一个订单要200毫秒,在库存量很高(10万台),并发量极高(每秒1000个请求),有个请求就要等待200秒了,这个肯定不能接受的。

解决办法:

1.库存分段

将10万库存,分成100段,每段1000个库存。对应的,就有100把锁去锁这100个库存段了,可以满足100个线程同时跑。

9a04aaaf6e87

image.png

这套方案确实可以解决高并发,高库存问题,然而库存分段也是个麻烦的事。

我这里还有个方案,虽然效率略低,但是跑起来应该还好。

2.库存占用

9a04aaaf6e87

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 {

}

}

执行前:

9a04aaaf6e87

image.png

9a04aaaf6e87

image.png

执行后:

9a04aaaf6e87

image.png

部分执行结果:

9a04aaaf6e87

image.png

剩下1个库存的时候,就跑步下去了。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP