为什么加了锁还是出现库存扣超的情况

最近碰到一个需求,库存扣减,要保证在高并发情况下不出问题。

第一想到的就是redis分布式锁,为了保证原子操作,加锁和解锁都使用lua脚本,但是在压力测试的时候发现,TPS只有个位数,而且成功率不好控制,我是自己控制尝试获取锁的次数,因此造成了大量的线程阻塞。

后面找了一些资料,改为redisson,TPS能达到100以上,成功率在90%以上,以为就此完结,后来自己做测试的时候发现数据总是有误,这里再提一下,业务并不是直接扣减库存,而是预扣减,我的做法是增加一条预扣减库存记录,在确认扣减后再进行真正的库存扣减,所以在每次有预扣减前先查询预扣减记录,用实际的库存减去预扣减记录之和后才是剩下的库存,但是在压测的情况下(100个线程循环10次,测试工具为jmeter),库存明明只有一个,居然出现多余一条的预扣减记录,也就是说实际库存是不够减的,排除了锁的问题,最后发现其实是插入预扣减库存记录的事务没有及时提交(用的ORM是spring-data-jpa),也就是说,第一个线程进来,插入了一条预扣减库存数据,但是并没有立即提交这个事务,然后释放锁,第二个线程进来查询预扣减数据并没有查到第一个线程插入的那条数据,于是库存数据就出错了。

在网上找了很多资料,找到了一种解决方案,就是spring的事务监听,等待事务提交后再释放锁,让新的线程进来,就不会出错了。

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    lock.unlock();
                }
            });

把查询与插入操作抽取到一个单独的方法中并加上事务注解,然后加上上面的这段代码,就可以保证在高并发下库存的正确性。

当然,这个监听也是个同步方法,会一直等待事务提交,所以性能上有些许的下降,但还能接受,至少在分布式部署的情况下高并发也不会出现库存错误的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值