商品库存扣减与数据一致性方案

前言

说一说博主遇到的问题,最近换了一家公司负责订单模块,看到之前写的库存扣减也是用redis思路实现,但是很容易就能看出破绽,以前没有做过电商起初的思考觉得是一个非常简单的功能吧,后面仔细想了一下深挖好像并没有想象的那么简单,就想思考出一种能够没有问题的思路,也参考了网上很多很多思路,可能思想性的东西谁也不会觉得谁的方案一定好,或者一定不好,索性就自己设计了一下留作备用,大家也可以借鉴思路。

表结构设计

首先引入2张表,对账表用于后期对出现的异常数据进行捕捉人工或自动补偿,最终一致性表用于redis扣减后异步定时任务
进行缓存与数据库写一致,当然可以不是定时器消息中间件也可以但是要保证类似rocketmq的事务消息机制即可

CREATE TABLE `goods_stock_log` (
  `id` bigint(20) NOT NULL,
  `before_num` int(32) DEFAULT NULL COMMENT '之前的库存',
  `after_num` int(32) DEFAULT NULL COMMENT '之后的库存',
  `num` int(32) DEFAULT NULL COMMENT '数量',
  `goods_id` bigint(20) DEFAULT NULL COMMENT '商品id',
  `type` tinyint(5) DEFAULT NULL COMMENT '1=下单(未支付,已支付) 2=商家增量 3=商家扣减 4=取消订单',
  `create_time` datetime DEFAULT NULL,
  `version` int(32) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商品库存对账表';

CREATE TABLE `stock_operation_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `before_num` int(32) DEFAULT NULL COMMENT '之前的库存',
  `after_num` int(32) DEFAULT NULL COMMENT '之后的库存',
  `num` int(32) DEFAULT NULL COMMENT '数量',
  `goods_id` bigint(20) DEFAULT NULL COMMENT '商品id',
  `state` tinyint(5) DEFAULT NULL COMMENT '0 = 待处理 1 = 完成',
  `type` tinyint(5) DEFAULT NULL COMMENT '1=下单(未支付,已支付) 2=商家增量 3=商家扣减 4=取消订单',
  `create_time` datetime DEFAULT NULL,
  `version` int(32) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='库存最终一致性表';

创建订单流程

在这里插入图片描述

最终一致性定时器

负责将redis扣减的库存写入到数据库中

在这里插入图片描述

如何对账

思路是取 T-1 天 与 T 当前天的数据,与redis缓存做对比

下面给出SQL语句

select 
	goods_id, sum(num) as num '余量'
from goods_stock_log a
where 
	 a.create_time between 前一天 and 当前天
	and b.create_time between 前一天 and 当前天
	group by goods_id
	

举个例子

假设现在,在不出现问题的系统情况下,对账表是如下纪录

在这里插入图片描述

如图可以看出前后数据的纪录能够完全对的上,接下来我们模拟出现了问题的数据是什么样子呢?

在这里插入图片描述

可以非常明显看出第五条纪录应该是6000开始但是为什么是从5997开始了呢?这就是可能流程图当中出现的可能性导致的,
redis库存比真实库存少导致的。
ps:这个金额不一定是之前的库存越来越少的情况可能出现一种情况,比如某次下单代码异常抛出,没有添加对账纪录,此时又来下单了,这次下单成功了,如下图就会出现不连续的对账所以,以数量变更之和来确定

在这里插入图片描述

上图是一种错乱情况,在就是redis扣减顺序的但是事务提交没法保证顺序流水也是无序的

对账流程

与最终一致性流程一致,通过定时器执行SQL就可以查找出这些"对不上的账",不管是后续通过发送机器人警报也好,还是人工补偿自动补偿都可以。
但是我觉得要注意的部分是,在对账期间需要根据对账表算出变更后的库存余量,根据redis做对比那么redis中库存的版本需要跟db的版本一致,因为如果你在对账的过程中有人下单了那么这样对账是不行的,需要在对账时间段,是禁止下单的,否则似乎这个对账无法对上
ps: 看见网上很多用lua脚本的 不太适合 redis集群的 lua脚本的需要所有哈希槽坐落的同一个节点下才能使用

考虑的问题

  1. 在对账期间,如果出现对账商品出现状态变更的情况,如何处理需要根据业务定夺。 比如在对账期间有用户对订单退款了那么将要对 redis db 同时做增量处理,目前想到的解决方案是,在【退款】【下单】【商家增量】【商家减量】都是由最终一致性定时器处理,对账定时任务负责统计对账表数据与redis数据是否一致,对账任务执行是不允许出现redis或者db数据变更的,否则版本不一致无法正确对账,也就是说对账过程中,既不允许做最终一致性处理,也不允许下单,也没有正在进行的下单。活着说是在对账过程中redis中的缓存版本和数据库中的库存版本不能在发生变动

为什么对账不允许下单如下图就能感知到对账过程中 查询数据库 然后set redis 如果在这个过程中下单了没感知到那么就会出现超卖
在这里插入图片描述

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值