读库存扣减系列文章有感
微信公众号架构师之路最近发了一篇关于库存扣减文章引起了大家的广泛转发,作为一个小菜鸟,也发表点自己的菜鸟想法吧
这篇文章原文是库存扣多了,到底怎么整 ,后面还有一篇对网友回复的解答库存扣减还有这么多方案?
第一篇文章中着重描述了扣减库存的并发问题如何解决,如何保证幂等。
文章首先解决的是如何做到幂等,因为“扣减”库存一定是一个非幂等的操作,那么可以通过“设置”库存来解决,因为设置库存是一个幂等操作。
第二个解决的是幂等之后的并发问题,因为“设置”库存虽然做到了幂等但是并没有解决并发时带来的一致性问题,当两个用户同时“设置”库存是会造成数据的不一致。
紧接着就引出了CSA(“Compare And Set”),CSA本质上就是一种乐观锁方式---先比较再设置。每次扣减库存时都做比较,如果“当前库存”总数和“原库存”总数一致时才执行“扣减”,否则“扣减执行失败”,那么失败后只有两种办法,要么事务回滚,要么自动重试,如果重试后发现扣减后总数小于0那么就需要返回给用户错误了。
这两个思路非常的nice,分别解决了“幂等”和“一致”,但是没有引出“高并发”这个话题,因为高并发下这种方法失败的概率很大,会频繁失败给用户不好的体验。
后来很多网友就在后面引出了另外的一些观点,例如redis,利用了redis的“快”,redis的快有几个原因,第一是内存读取,第二是非阻塞IO,第三是单线程loop,避免了物理锁,减少线程上下文切换时间,第四是hash结构存储。因此虽然是单线程,但是redis带来非常高效率的读取,并且天然支持“高并发”,因为单线程操作不加任何锁。但是redis的另外一个风险就是数据存储在内存中,有丢失的风险。因此,使用时还需结合业务场景来看。
第一篇文章后面还有网友提到了使用队列来异步“扣减”,这实际上就和redis单线程一样了,这种方法思路挺好的,但是本菜鸟觉得会损失掉很多性能,是一种时间换空间的方案,可能会带来用户体验问题。
着重要提到的是后面一篇文章有一个答复思路非常新颖,因为这个问题的并发点实际上就是“库存”,所有的请求都在这个单点上操作,因此采用分布式的思路就是把这个“单点”均衡开,如果库存总数是m,那么将同一个key下的库存分成n组,k1...kn,每组的库存数为m/n,扣减时可以将请求均分到k1到kn上,这样就减少了冲突的几率,又是一种空间换时间的好方法!
最后本菜鸟想说的是,库存扣减这个问题往深了说在每一层可能都有不同的解决方案,在应用场景中应该按照具体的场景来看,是多读场景还是多写场景,用户的要求也不一定相同,因此业务场景决定了技术选型。这个话题还是非常值得探讨的,各种思路产生了火花,希望博主今后能多发些这种好文^_^