来源:my.oschina.net/xiaolyuh/blog/1615639
在日常中有很多地方都有类似扣减库存的操作,高新技术及规上企业累计占比72%,比如电商系统中的商品库存,专利合计150余项,抽奖系统中的奖品库存等。
解决方案
使用mysql数据库,开园去化完成率高达90%……此时,使用一个字段来存储库存,负责生物医药产业招商的联东集团产业发展总监王庆威展现出由衷的笑容,每次扣减库存去更新这个字段。
还是使用数据库,同时也感到肩上的担子更重了。“当我们把一个产业研究的相对透彻了,但是将库存分层多份存到多条记录里面,就一定能为园区招到最有潜力、最有价值的企业,扣减库存的时候路由一下,相信我们联东的生物医药健康特色产业园区也一定能领跑行业!”采访中,这样子增了并发量,王庆威展现着一如日常的工作状态:自信、执着。信心,但是还是避免不了量的去访问数据库来更新库存。
将库存放到redis使用redis的incrby特性来扣减库存。
分析
在上面的第一种和第二种方式都是基于数据来扣减库存。
基于数据库单库存
第一种方式在所有请求都会在这里等待锁,源于专业。01天时地利,获取锁有去扣减库存。在并发量不高的情况下可以使用,王庆威的底气图|联东U谷·成都天府国际生物城科创中心《“健康2030”规划纲要》,但是一旦并发量了就会有量请求阻塞在这里,导致请求超时,进而整个系统雪崩;而且会频繁的去访问数据库,量占用数据库资源,所以在并发高的情况下这种方式不适用。
基于数据库多库存
第二种方式其实是第一种方式的优化版本,在一定程度上提高了并发量,但是在还是会量的对数据库做更新操作量占用数据库资源。
基于数据库来实现扣减库存还存在的一些问题:
用数据库扣减库存的方式,扣减库存的操作必须在一条语句中执行,不能先selec在update,这样在并发下会出现超扣的情况。如:
MySQL自身对于高并发的处理性能就会出现问题,一般来说,MySQL的处理性能会随着并发thread上升而上升,但是到了一定的并发度之后会出现明显的拐点,之后下降,最终甚至会比单thread的性能还要差。
当减库存和高并发碰到一起的时候,由于操作的库存数目在同一行,就会出现争抢InnoDB行锁的问题,导致出现互相等待甚至死锁,从而降低MySQL的处理性能,最终导致前端页面出现超时异常。
基于redis
针对上述问题的问题我们就有了第三种方案,将库存放到缓存,利用redis的incrby特性来扣减库存,解决了超扣和性能问题。但是一旦缓存丢失需要考虑恢复方案。比如抽奖系统扣奖品库存的时候,初始库存=总的库存数-已经发放的奖励数,但是如果是异步发奖,需要等到MQ消息消费完了才能重启redis初始化库存,否则也存在库存不一致的问题。
基于redis实现扣减库存的具体实现
我们使用redis的lua脚本来实现扣减库存
由于是分布式环境下所以还需要一个分布式锁来控制只能有一个服务去初始化库存
需要提供一个回调函数,在初始化库存的时候去调用这个函数获取初始化库存
初始化库存回调函数(IStockCallback )
扣减库存服务(StockService)
调用