商品和订单服务间使用MQ

商品服务的库存变化时,通过 MQ 通知订单服务库存变化。

原始的同步流程

查询商品信息 (调用商品服务)

计算总价(生成订单详情)

商品服务扣库存(调用商品服务)

订单入库( 生成订单)

// 原始的MySQL同步流程

// 判断此代金券是否加入抢购

SeckillVouchers seckillVouchers = seckillVouchersMapper.selectVoucher(voucherId);

AssertUtil.isTrue(seckillVouchers == null, "该代金券并未有抢购活动");

// 判断是否有效

AssertUtil.isTrue(seckillVouchers.getIsValid() == 0, "该活动已结束");

// 插入数据库

seckillVouchersMapper.save(seckillVouchers);

在订单生成时直接扣库存,这是最原始的扣库存方案,比较简单,但存在问题

可能导致很多订单把产品库存扣除而未支付,这就需要有一个后台脚本,将一段时间内没有支付的订单的库存释放,把订单取消即时扣库存,并发差

1、3步商品服务,操作商品服务的 db,2、4步订单服务,操作订单服务的 db。

避免访问不同服务的 db,原则上同一服务只能操作自身服务的 db。

MQ异步化

首先考虑只将第4步异步。

分析

2,4都是操作db,第4步不再等待,1、2、3成功后立即反馈给用户。

之后通过消息通知服务异步下单,若第4步异步下单失败,重试操作,试图重新生成订单,MQ的消息也可回溯。

订单创建完成后,处于排队状态,然后服务发布一个事件Order Created 到消息队列中。

即订单服务向外界发送消息:我创建了一个订单,由MQ 转发给订阅该消息的服务

如果商品服务收到创建订单消息之后执行扣库存操作。注意,这里可能因为某些不可抗因素导致扣库存失败,无论成功与否,商品服务都会发送一个扣库存消息到 MQ,消息内容即扣库存的结果。

订单服务会订阅扣库存的结果,接收到该消息后:

如果扣库存成功,将订单的状态改为已确认,即下单成功

如果扣库存失败,将订单的状态改为已取消,即下单失败

欲实现上述模型要求,需可靠的消息投递。服务发出的消息,一定会被MQ收到。

用户体验的变化,前端配合排队中等界面。

商品/订单服务都变成异步化,适合秒杀类场景,当流量不大时,并不太适合。

异步设计

库存在Redis中保存

收到请求Redis判断是否库存充足 ,减掉Redis中库存

订单服务创建订单写入数据库,并发送消息

当订单支付成功后,会有一个出库过程,既然有这个过程,就有可能出库失败。

库存有两部分:

缓存redis层

数据库mysql层

当客服新增5个库存,则缓存redis和数据库mysql层都需增加5个库存,使用分布式事务的最终一致性来满足:库存要么全加,要么全不加。

当订单生成时,需要扣除库存,先扣redis库存,如果扣除成功,则生成订单进行支付,这个过程不扣除mysql库存。

当redis库存扣完,该产品就无法下单了,下单就会失败,就把外层的给挡住了。

在第2步扣除redis库存成功后,生成订单,进行支付,支付成功,返回我的订单中心, 会发现有一个出库过程。

出库过程一个MQ异步解耦的任务队列,这个过程是扣除mysql库存:

如果扣mysql库存成功,出库成功,完成下订单整个流程,进入发货状态

如果扣mysql库存失败,出库失败,进行一系列的操作

订单状态改成取消

返还redis库存

退款

redis库存和mysql库存

支付前是预扣,是扣redis库存,是锁定库存的过程

支付后是真正扣,扣mysql库存,保证库存最终一致

但是,在极端情况下会存在数据不一致

如果redis库存 = mysql库存,不会有问题

如果redis库存 < mysql库存,不会有超卖问题,但会存在实际有库存,但是没有卖的情况

如果redis库存 > mysql库存,就会超卖,超卖的订单,在出库的过程中会失败

这样总体不会出问题,mysql数据库层,保证库存最终不会出问题。

问题

数据库库存和redis库存不一致,如何检测?

如果检测出来不一致,如何同步

没有想出来好的方案

比较暴力的方式,就是找一个低峰期,譬如凌晨1点,周期性强行覆盖。 但是极端情况下还是会存在同步后不准确,譬如在同步的过程中,刚好有一个订单在支付,这个订单支付成功后,出库的过程中,扣除了mysql的库存,但是没有扣除redis的库存

这个就是数据库同步缓存的更新机制方面的问题

属于一致性的逻辑设计的问题

缓存数 = 数据库库存数 - 待扣数

当然这里面也还有其它的方案,以及考虑到一致性的要求高低,可以使用简单或复杂的方案

就看系统复杂度了,越是大系统就要拆得越细

比如待扣数又可以放到一个队列里面,或者缓存里面,同时有计数,直接读计数就行

比如放到mongo,已支付待出库的数量,一般也不会很大,count一下,也不会损失多少

所以一般系统都不能完全保障数据链不出错,但一定要有补偿,就是出错了可以纠错

要保障不出错的代价显然太大

同步是有一套刷新机制,可以定时,也可以通过MQ,或者监控不一至同步等等。。。

也叫做保障缓存数据的新鲜度

一般不会太长时间,半小时,几分钟都有可能,不同场景需求不一样

12306

买火车票的12306,晚上的时间都不能买票,这个时间估计是在同步库存,将数据库库存同步到redis库存中, 但是买火车票之类,在订单生成前,必须扣除实际库存,也就是要扣除mysql的库存,

因为买火车票和购物不一样,购物可以付款后出库,但是买票这种,支付前就必须出库,因此,要将出库过程提前, 只有出库成功,才能生成订单,同样要引入redis库存

先扣缓存中的库存,扣除成功后,然后才可以去扣mysql中的库存。

如果扣除缓存中的库存失败,就会挡在外面,返回库存不足,这些请求不会穿刺到mysql中,挡住了大多数的请求压力。

redis库存会和mysql库存不一致,极端情况下是肯定有的,需要进行库存同步

当缓存库存比数据库库存多,那么就会出现,查询有票,但是就无法下单,下单的时候就说库存不足,这个情况下,就会造成数据库压力过大,不过12306应该有其他手段来规避这个问题,不过,我确实遇到过,查询的时候有票,但是无法下单的情况。

当缓存库存比数据库缓存少,那么不会出问题,只会出现有票,但是没有出售的情况,等完成库存同步一下, 明天又准确了。

到此这篇关于Redis解决库存超卖问题实例讲解的文章就介绍到这了,更多相关Redis解决库存超卖问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

JAVA中如何解决超卖,Redis解决库存超卖问题实例讲解相关推荐

  1. java 转换gbk编码,java中GBK转UTF-8乱码的解决方法

    java中GBK转UTF-8乱码的解决方法 如果自己采用的是GBK编码,对方采用得到是UTF-8编码,发送数据时需要将GBK编码数据转换成UTF-8编码数据,这样对方才不会乱码. 问题出现:GBK转U ...

  2. java中的方法求和_在Java中模拟求和类型的巧妙解决方法

    java中的方法求和 在继续阅读实际文章之前,我想感谢令人敬畏的Javaslang库的作者Daniel Dietrich ,他在我面前有了这个主意: @lukaseder尝试使用静态方法<T,T ...

  3. 在Java中模拟求和类型的巧妙解决方法

    在继续阅读实际文章之前,我想感谢令人敬畏的Javaslang库的作者Daniel Dietrich ,他在我面前有了这个主意: @lukaseder尝试使用静态方法<T,T1扩展T,... Tn ...

  4. java中的类和对象(重点)超详细

    java中的类和对象 1.类与对象的初步认知 2.类和对象的实例化 3.类的成员 3.1.字段,属性,成员变量 3.2.方法 3.3.static 关键字 3.4.小结 4.封装 4.1.privat ...

  5. python中locals函数_Python神奇的内置函数locals的实例讲解

    摘要 本文我们介绍神奇的locals函数,包括动态创建变量和动态访问变量,以及一个应用场景. 相同属性不相邻问题 需求:有两个list,分别为list1和list2.list1中有n个对象,每个对象有 ...

  6. java商品搜索功能_利用solr实现商品的搜索功能(实例讲解)

    后期补充: 为什么要用solr服务,为什么要用luncence? 问题提出:当我们访问购物网站的时候,我们可以根据我们随意所想的内容输入关键字就可以查询出相关的内容,这是怎么做到呢?这些随意的数据不可 ...

  7. java判断三位数的范围代码_java判断三位数的实例讲解

    java判断三位数的实例讲解 java怎么判断三位数 先定义个测试数字,如图 然后可以把数字转换成字符串来判断它的长度是否为3,如图 获取判断数字范围是否在100到1000之间的值,如图 在或者判断数 ...

  8. Java中死锁产生的原因及解决方法

    一.什么是死锁 死锁就是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的现象,若无外力作用,他们都无法推进下去. 简单来说就是A和B若同时都有一个资源,在此之外还想拥有对方的资 ...

  9. java变量同名_浅析Java中局部变量与成员变量同名解决技巧

    要想区分这哥俩,首先,我们得知道它们分别是什么.先从成员变量下刀. 成员变量 我们来研究一个事物: 属性:外在特征:例如人的身高,体重 行为:能够做什么:例如人有说话,打球等行为. 而在Java语言中 ...

  10. java redis 命令_命令界面:使用Java中的动态API处理Redis

    java redis 命令 Redis是一个数据存储,支持190多个文档化命令和450多个命令排列. 社区积极支持Redis开发: 每个主要的Redis版本都附带新命令. 今年,Redis向第三方供应 ...

最新文章

  1. POJ 2135 Farm Tour amp;amp; HDU 2686 Matrix amp;amp; HDU 3376 Matrix Again 费用流求来回最短路...
  2. poi设置自动换行后显示不全_WPS表格:如何让数据超过单元格就自动换行并完全显示?...
  3. 一夜吸粉200万被封杀,微信都有哪些逆鳞?
  4. 表格大小设置_系统地学习Excel第18课,设置单元格字体格式
  5. linux python开发环境_如何在Linux系统中搭建Python编程环境
  6. u盘无法复制文件进去_只需一招,禁止Windows复制文件到U盘,再也不用担心你的资料被拷走!...
  7. mysql服务2013错误_错误2013(HY000):在“读取授权数据包”时丢失与MySQL服务器的连接,系统错误:0...
  8. autohotkey编写windows脚本实现test.lab试验数据快速导出
  9. 1000个摄像头的网络怎么搭建?为什么500个就卡的不行?
  10. centos7.6 LNMP新版本
  11. oracle rac防护,Oracle RAC日常基本维护命令
  12. 编程十年 (3):初识计算机
  13. 2020年 2月 省市区sql(一)
  14. java计算机毕业设计西藏民族大学论文管理系统源程序+mysql+系统+lw文档+远程调试
  15. 如何选择外贸网站服务器?
  16. BZOJ 1984: 月下“毛景树” [树链剖分 边权]
  17. android图片背景颜色透明度,android:设置背景图片、背景颜色透明
  18. 验证码错误的可能问题
  19. 软件可靠性计划过程组成与LRU简介
  20. SpringAnimation弹簧动画简单使用(个人学习记录)

热门文章

  1. ios 自己服务器 苹果支付_修复苹果IOS支付
  2. 各互联网公司offer比较
  3. vue打包多个html,vue多页面应用打包配置
  4. coldfusion_ColdFusion教程:第一部分
  5. 电信副卡显示无服务器,电信副卡,你从来就是一个陷阱吗?
  6. Dubbo-接口数据序列化Serialization
  7. 阿里P7晒出1月工资单:狠补了这个,真香...
  8. Win10激活(家庭版升级到专业版)带你5分钟解决
  9. hadoop cgroup源码解读
  10. 腾讯云服务器操作系统TencentOS的正确安装方法