库存服务
一、 概述
- 为什么要做库存系统
下单系统面临高并发场景时,修改库存操作成为下单时的瓶颈之一,因为在数据库这层本质执行的是对同一件商品扣库存。
如今在电商行业里,秒杀抢购活动已经是商家常用促销手段。但是库存数量有限,而同时下单人数超过了库存量,就会导致商品超卖甚至库存变负数的问题。 又比如:抢购火车票、论坛抢楼、抽奖乃至爆红微博评论等也会引发阻塞式高并发问题。如果不做任何措施可能在高瞬间造成服务器瘫痪,如何解决这个问题呢?
库存超买问题描述:
高并发:某些时段同时申领的人过多,导致一些专柜的存货为负数 - 技术实现的想法和解决方案
先查询判断库存是否充足,然后扣减库存;并发场景就会造成库存为负,这是最低级的场景。
判断和扣减一个sql进行,并判断减后数量;并发场景数据库压力太大,此时就要考虑降压。事务时控制超买的必要条件但不是充分必要条件。
法一:流控。分两种,接口层:只允许部分请求,其他的提示稍后再试;商品层:多商品场景,需考虑每个商品热度不一样,按需控制。
法二:ngix分服务器分流。
法三:缓存。库存数量放在redis上,先判断库存是否足够能否来下单,不能下单的直接挡掉请求。进来的请求还是很多呀,那就队列、消息系统来缓冲吧。要考虑一个问题,redis库存与数据库库存数是否一致。
法四:异步消息,异步处理操作库存数据库的操作,此时要保证幂等性。
法五:将提交操作变成两段式,先申请后确认。第一段在redis处理,第二段在数据库处理。
log排重。如果改订单已经有过一次提交,拦掉,避免重复操作。
锁:悲观锁、乐观锁、缓存上的分布式锁、zookeeper锁。用来解决互斥操作,比如调整库存时无法下单,预扣库存后解冻及实扣不能同时允许,只能有一个执行成功。不建议加数据库锁。 - 前端
控容
静态化
限流
有损服务 - 数据库
强烈建议用户开启数据库线程池功能(注意:不是连接池):线程池的机制是每个用户的连接并不是一定会产生一个实际的硬连接,而是通过Pool机制从中进行分配,也就是实际在数据库内部运行的线程数是固定的,减小上下文切换的时间,从而大幅提升数据库的性能。
二、产品配置 - 产品配置
配置商品信息、库存销售模式、预警信息等
库存销售模式说明:
2.1 配置样例
- 基本模式
"BS0001": {
"sellMode": "BS",
"partitionNumber": "0",
"deductMode": "1",
"isMergeRedisInv":"0",
"mergeThreshold":"0"
} - Redis模式
// Redis同步
"RS0052": {
"sellMode": "RS",
"partitionNumber": "5",
"deductMode": "2",
"isMergeRedisInv":"1",
"mergeThreshold":"10"
}
// Redis异步
"RS0052": {
"sellMode": "RS",
"partitionNumber": "5",
"deductMode": "2",
"isMergeRedisInv":"0",
"mergeThreshold":"0"
}
2.2 配置规则说明
NOTE:销售模式模板号的生成规则:(销售模式 + 三位分桶数 + 扣减模式)或(sellMode + partitionNumber + deductMode)
合桶规则说明:
1)RS模式下库存桶数partitionNumber >1,
2)设置isMergeRedisInv = 1,
3)某个key低于该阈值mergeThreshold,或某个订单扣减超过某个桶的剩余库存,发现不够扣了。
把所有key的库存合到一个key上。
2. 调整库存
2.1 调增销售库存,如正品库存、虚拟库存
2.2 调减销售库存,如正品库存、虚拟库存
2.3 调增/调减其他库存,如瑕疵品库存
三、下单场景
- 下单模式说明
分两种对接模式:
预扣模式-生成订单前预扣库存,支付成功后再实际扣减库存;支付失败,等待取消订单时解冻库存,或再次发起支付。
直扣模式-支付前直接扣减库存,支付成功后结束;支付失败后冲正
出库流程一致