概述
商品库存扣减与数据一致性方案
- 前言
- 表结构设计
- 创建订单流程
- 最终一致性定时器
- 如何对账
- 举个例子
- 对账流程
- 考虑的问题
前言
说一说博主遇到的问题,最近换了一家公司负责订单模块,看到之前写的库存扣减也是用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脚本的需要所有哈希槽坐落的同一个节点下才能使用
考虑的问题
- 在对账期间,如果出现对账商品出现状态变更的情况,如何处理需要根据业务定夺。 比如在对账期间有用户对订单退款了那么将要对 redis db 同时做增量处理,目前想到的解决方案是,在【退款】【下单】【商家增量】【商家减量】都是由最终一致性定时器处理,对账定时任务负责统计对账表数据与redis数据是否一致,对账任务执行是不允许出现redis或者db数据变更的,否则版本不一致无法正确对账,也就是说对账过程中,既不允许做最终一致性处理,也不允许下单,也没有正在进行的下单。活着说是在对账过程中redis中的缓存版本和数据库中的库存版本不能在发生变动,
为什么对账不允许下单如下图就能感知到对账过程中 查询数据库 然后set redis 如果在这个过程中下单了没感知到那么就会出现超卖
最后
以上就是粗犷歌曲为你收集整理的商品库存扣减与数据一致性方案的全部内容,希望文章能够帮你解决商品库存扣减与数据一致性方案所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复