电商库存系统一
概述
库存是一个复杂度很高的核心系统,提供前端实时查询的能力和后端的商品扣减,还要处理订单取消问题。这对库存系统的准确性和可靠性提出了很高的要求,在各种场景下能保证数据的准确,在遇到突发流量时能保证高可用,除此之外,还需要提供熔断和降级的能力,保证在极端情况下不拖垮主站。
技术架构
DUBBO是一款非常优秀的 RPC 层框架,提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
RocketMQ是一款低延迟、高可靠、可伸缩、易于使用的消息中间件。
Redisson是架设在Redis基础上的一个Java驻内存数据网格。
Redis单线程的高速缓存数据库,支持持久化、事务,通过哨兵和自动分区提供高可用。
Druid阿里的开源软件,专为监控而生的数据库连接池。
Mybatis是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
MySQL关系型数据库系统。
库存扣减
为简化库存系统,库存系统之上建立交易系统和订单管理系统,库存只负责管理商品扣减。
1. 何时扣减
库存扣减一般有三个时机:
加购物车时扣减。商品加入购物车,并不能说明用户确实要购买,实际转化率并不高,假如此时扣库存,会导致库存被占用而无法释放,影响正常销售。
提交订单时扣减。用户下单后还有一个支付操作,如果用户仅仅下单而不进行支付,这部分库存将会被无效占用,影响正常销售。这里可以做一个支付时限,如果超过时限未支付,订单将被取消,预占得库存被返还。
支付时扣减。用户进行支付,说明交易即将完成,这部分是必须要留住的订单和用户,如果此时扣库存,出现库存不足导致用户支付失败会严重影响用户体验。
目前为提高库存效率和用户体验,系统采用提交订单扣库存的策略。这也是大部分电商平台采用的扣减策略。
2. 如何扣减
库存扣减需要应对主要问题是高并发问题、重复扣减问题(超卖)。
上游系统需要配合库存系统做好扩容、限流保护、隔离(业务隔离、数据隔离,以及系统隔离)、动静分离等措施,通过漏斗模型逐层减少用户流量,避免流量高峰对库存系统的冲击,必要时采用熔断进行降级(比如查询库存时直接返回有库存)。
系统采用Redission+Redis解决高并发问题,Redis是内存数据库性能相对较高,本身支持事务操作,使用Redisson的Rbatch保证事务性,扣减操作包含query和done,两个动作属于一个事务,且done操作保证幂等。
重复扣刷减最常出现的包括用户误操作、黄牛绕过前端进行单、交易系统重试操作:
用户误操作,前端购物车在用户提交订单后将购物车数据删除,无法继续提交。
黄牛绕过前段刷单,前端必须经过交易系统通过RPC调用库存系统,在交易系统中通过风控、调用次数限制、订单令牌等方法限制重复提交订单。
交易系统重试问题,库存系统通过限制一个订单只能进行一次事务性的库存扣减操作,防止交易系统重试导致的重复扣减。
还有重要的一点是记录库存扣减日志,在库存回滚时使用。
3. 如何回滚
用户取消订单、用户退货、异常订单流程都会触发库存回滚操作。
为每一个订单设定一个唯一编号,并且按照编号记录库存操作流日志,在需要做库存回滚时依据唯一编号进行库存的返还,回滚操作需要根据编号保证事务性和幂等性。
库存的返还采用同步回滚+异步重试机制,用户取消订单和异常订单的回滚操作首先进行订单的同步逆向操作,在库存返还时如果失败,将唯一编号记录下来,通过异步定时任务进行返还。
用户退货往往涉及到实物商品的退库,因此发起方是订单管理系统,当实物商品完成退库后,订单管理系统将发送MQ消息,库存系统通过订阅消息进行库存返还。
尽管同步回滚+异步重试已经足够保证库存的正确回滚,但是为万无一失,还需要增加一个数据健康度的自我检查能力,超时超次未完成回滚的库存数据需要人工干预。
数据库
1.Redis
由于Redis的读写能力远胜于任何关系型数据库,因此在Redis中保存商品库存数据并完成扣减操作。
使用Redis的以下特性保证库存系统的高并发和高可用:
分库分表,因垂直电商业务特点且主站秒杀和抢购并不在主站中,因此为简化逻辑按照SKU维度采用HASH算法进行分库的策略。当然在极端情况下可以对每个SKU继续进行库存拆分,拆分成二级SKU,目前秒杀业务就是采用这种策略。
读写分离,采用Master/Slave模式,其中Master提供写操作,Slave提供读操作,降低Master的压力,提供高速读写操作。
主从+哨兵模式,为保证Redis的可靠性,启用Redis的主从模式和哨兵模式。部署Sentinel集群+Master/Slave集群,实现Master故障的自动发现和切换。
持久化,启用AOF实时持久化功能,保障数据的秒级备份。
2.MySQL
用于记录操作日志和扣减日志。
为提高读写性能采用主从模式和读写分离模式,最初MySQL还承担着库存数据的批量落盘功能,随着Redis库存方式的长时间稳定运行,库存逐渐完全转移到Redis上,MySQL仅仅承担着记录操作日志和扣减日志的功能。