热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【高并发】生产级系统搭建4

前言关于高并发系统中,当前比较热门的还是属于“秒杀”系统,前面章节在整理了“秒杀”系统的相关设计概念后,本章节,来讲解扣减库


前言

    关于高并发系统中,当前比较热门的还是属于“秒杀”系统,前面章节在整理了“秒杀”系统的相关设计概念后,本章节,来讲解扣减库存相关的业务逻辑。


1 库存的那些事

    一般电商网站中,购买流程一般都是这样的:商品列表页看重某个商品,然后进入详情页浏览商品详细信息,有购买意向后,单击“支付”按钮(或者将商品加入购物车,从购物车进行结算),网站弹出支付方式让用户进行选择支付方式,并进行支付。

    对于这样的购买流程,用户在下单时,一般不需要关心库存的,只有没有库存了,用户才会感到没有库存了。但是在高并发场景中,这样的购买流程就会出现问题。例如,在“秒杀”活动中,用户会争抢库存,如果没有控制好,则会出现“超卖”的情况。会给公司造成损失。

(对于“秒杀”活动,一般公司是不允许商品“超卖”的,即【下单成功的数量】不能大于【商品库存数量】。一旦出现“超卖”,公司则会出现损失。如果被恶意流量利用,则损失可能是巨大的。)


1.1 扣减库存方式

    库存对于电商平台来说,是一个重要的业务指标,所以在技术上需要合理设计扣减库存,不能出现“超卖”的情况。

一般扣减库存常有以下3种方式:


  • 下单后扣减库存:用户在下单后就扣减库存,这种扣减方式最简单,也最好理解。但是问题是,可能有用户下单后不付款,特别是竞争对手利用“秒杀器”大量抢购商品,但是不支付。如果是这样,那商家就没有达到活动的真正目的,且真正想得到商品的用户也无法得到低价商品。


  • 支付后扣减库存:在用户付完款后再扣减库存,这样可以解决用户“下单但不支付”的情况。但是这种方式也有问题:会发生【超卖】情况。因为,在瞬时洪峰流量下,会有很多用户付款成功,但是只有一小部分用户能真正抢到商品,大部分用户在付完款后系统会报“已售罄”或“活动结束”等。

(对于“支付后扣减库存”这种方式,当出现库存不足时,有些商家是可以后续再补货的)


  • 预扣减库存:在用户下完订单后,系统会为其锁定库存一段时间,例如30分钟;在超过锁定时间后,会自动释放锁定的库存,让其他用户抢购。当用户付款时,系统会校验库存是否在锁定有效期内,如果在有效期内,则可以进行支付;如果锁定有效期已过,则重新锁定库存,若锁定失败则报“库存不足”的提醒。

(在用户下单后,有些平台会有一个支付时间的倒计时。例如12306网站,会有一个30分钟的有效支付时间,这种扣减库存方式相当于“预扣减库存”方式。)


1.2 在千万级流量的“秒杀”中,如何扣减库存?

    上面了解到,有三种扣减库存方式:下单后扣减库存、支付后扣减库存 以及 预扣减库存。对于“秒杀”业务,应当如何选择扣减库存的方式呢?

    由于在“秒杀”场景中,商品一般对用户具有很强的吸引力,所以,在这种场景中使用“下单后扣减库存”方式更为合适。

(在“秒杀”场景中,大部分用户抱着“抢到就是赚到”的想法,一般都会付款,但是如果真的有竞争对手恶意下单不付款,应该怎么办?前面在流量管控中已经说到,可以对请求日志进行实时分析,让风控系统选择出恶意用户,然后将其封停。)

    因此,对于“下单后扣减库存”这种方式,利用数据库的事物特性,可以保证订单和库存扣减数量的一致性。

    下面来分析该如何去做好“下单后扣减库存”,防止商品被“超卖”。

(1)将某个商品的库存数量查询出来:

select num from stock where prod_id = $prod_id

(2)更新库存。用【库存数量】减去【购买的商品数量】,然后将结果值更新到库存数据库中。例如,商品库存数量为10,之后用户购买了2件,则d得到的结果值是:10 - 2 = 8,只需要将 8 更新为最新的库存数量即可。

问题:为什么是更新“结果值”的操作,而不是直接更新“扣减”的操作呢?

回答:因为,“扣减”不是幂等的,如果接口设计的不够完善,没有考虑到幂等性,那么在由于网络原因或者其他原因重试后,会出现重复“扣减”,即会出现“超卖”,甚至库存为负值的情况。


    通过上面两步的处理,基本可以保证下单扣减库存的准确性,但是对于“秒杀”这样的高并发场景来说这样还是有风险的。例如,在大流量、高并发下,有两个用户同时抢购,都拿到了库存数量为10的商品,其中一个用户购买了5件商品,随后更新库存数量为:10 - 5 = 5 件;接着另外一个请求购买了3件商品,随后更新库存数量为:10 - 3 = 7件。

(在高并发场景中,有可能出现并发更新数据不一致的情况,对于上图的两次请求,正常来讲应该卖出8件商品,但是现在却卖出了3件商品,因为update set动作覆盖了之前的数据。)


    在更新库存数量时,需要将【当前库存数量】与【之前的库存数量】进行对比:

update stock set num = $new_num where prod_id = $prod_id and num = $old num

    有了这种对比,在并发更新时,用户A和用户B只有在更新提交前查询到的库存数量为10,才能成功更新库存数量。

但是,在这种并发场景中,如果没有进行对比,则会出现以下问题:


  • 开始库存数量为10,第一个用户更新库存数量是成功的。

  • 在第一个用户更新完库存数量后,库存数量变成了5,所以,这时另一个请求是不能扣减成功的,在代码中会提示更新失败或者提示用户扣减库存失败的提示。


1.3 在千万级流量的“秒杀”中,优化库存数量扣减方案

    在“秒杀”场景中,通过流量分层控制可以分层管控大量的“读”请求。但是,依然会有很大的流量进入真正的下单罗技。对于这么大的的流量,除了前面说的数据库个例外,还需要进一步优化库存数量,否则数据库读/写依然是系统的瓶颈。

(1)利用好缓存。

    在“秒杀”场景中,如果只是一个扣减库存数量这样简单的流程,则可以先将库存数量直接放在缓存中,然后利用分布式缓存(如Redis)去应对这种瞬时流量洪峰下的系统挑战。

ps:使用缓存是存在一定风险的,例如,缓存节点出现异常,那库存数量应该怎么计算?使用缓存不仅要考虑分布式缓存高可用,还要考虑各种限流容错机制,以确保分布式缓存对外提供服务。

(2)异步处理。

    如果是复杂的扣减库存(如涉及商品信息本身或者牵连其他系统),则建议使用数据库进行库存数量的扣减,可以使用异步的方式来应对这种高并发的库存数量更新。


  • 在用户下单时,不like生成订单,而是将所有订单依次放入队列。

  • 下单模块依据自身处理速度,从队列中依次获取订单进行“下单扣减库存”的操作。

  • 在订单生成成功后,用户即可进行支付操作了。

    这种方式是针对“秒杀”场景的,依据“先到先得”的原则来保证公平公正,所有用户都可以抢购,然后等待订单处理,最后生成订单(如果库存不足,则生成订单失败)。这样的逻辑,对于用户来说体验感不是很差。具体排队逻辑如图:

(对于用户来说,只需要在商品活动详情页中提交一次抢购请求,之后就是等待系统处理进度。当然,这个等待时间是要设计的,不要让用户等待太久的时间,不管成功或者失败。)



    上面介绍了千万级流量“秒杀”系统的基本架构、“秒杀”系统的设计原则、如何做动静分离方案和流量控制,以及扣减库存方案的内容。这些都是设计高并发“秒杀”系统必须要考虑的。

    “秒杀”系统的流程并不复杂,只是一个“下单扣减库存”的动作,但是由于其具有独特的业务特点,所以在进行系统设计时千万不能大意。对于瞬时流量洪峰的高并发“秒杀”系统,我们可以总结一下:

(1)数据的静态化技术,用来应对高并发读的请求。


  • 各层级缓存的处理(即多级缓存的技术)。

  • 分布式缓存技术。

(2)负载均衡反向代理技术。


  • LVS。

  • Nginx。

(3)异步处理技术。


  • 消息队列技术。

  • 排队系统技术。

(4)系统架构设计技术。


  • 系统模块化划分。

  • 微服务架构思想。

(5)系统监控技术。


  • 日志监控。

  • 服务监控。


    本章节主要讲解了库存扣减相关设计经验,在面对库存扣减这一业务,不能大意,需要认真进行系统设计、业务设计,选择合理的方案来解决库存扣减的场景。以及在最后进行了一个简单的前面章节的统一汇总,希望小伙伴们能够及时回顾前面章节内容。

    下一章节会针对系统交互过程中的服务通信设计进行讲解。希望大家能够分享公众号给身边的小伙伴,大家一起来学习



推荐阅读
  • 作为140字符的开创者,Twitter看似简单却异常复杂。其简洁之处在于仅用140个字符就能实现信息的高效传播,甚至在多次全球性事件中超越传统媒体的速度。然而,为了支持2亿用户的高效使用,其背后的技术架构和系统设计则极为复杂,涉及高并发处理、数据存储和实时传输等多个技术挑战。 ... [详细]
  • 2019年后蚂蚁集团与拼多多面试经验详述与深度剖析
    2019年后蚂蚁集团与拼多多面试经验详述与深度剖析 ... [详细]
  • 探讨Redis的最佳应用场景
    本文将深入探讨Redis在不同场景下的最佳应用,包括其优势和适用范围。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • MySQL的查询执行流程涉及多个关键组件,包括连接器、查询缓存、分析器和优化器。在服务层,连接器负责建立与客户端的连接,查询缓存用于存储和检索常用查询结果,以提高性能。分析器则解析SQL语句,生成语法树,而优化器负责选择最优的查询执行计划。这一流程确保了MySQL能够高效地处理各种复杂的查询请求。 ... [详细]
  • 服务器部署中的安全策略实践与优化
    服务器部署中的安全策略实践与优化 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 在当今的软件开发领域,分布式技术已成为程序员不可或缺的核心技能之一,尤其在面试中更是考察的重点。无论是小微企业还是大型企业,掌握分布式技术对于提升工作效率和解决实际问题都至关重要。本周的Java架构师实战训练营中,我们深入探讨了Kafka这一高效的分布式消息系统,它不仅支持发布订阅模式,还能在高并发场景下保持高性能和高可靠性。通过实际案例和代码演练,学员们对Kafka的应用有了更加深刻的理解。 ... [详细]
  • 2021年Java开发实战:当前时间戳转换方法详解与实用网址推荐
    在当前的就业市场中,金九银十过后,金三银四也即将到来。本文将分享一些实用的面试技巧和题目,特别是针对正在寻找新工作机会的Java开发者。作者在准备字节跳动的面试过程中积累了丰富的经验,并成功获得了Offer。文中详细介绍了如何将当前时间戳进行转换的方法,并推荐了一些实用的在线资源,帮助读者更好地应对技术面试。 ... [详细]
  • 提升 Kubernetes 集群管理效率的七大专业工具
    Kubernetes 在云原生环境中的应用日益广泛,然而集群管理的复杂性也随之增加。为了提高管理效率,本文推荐了七款专业工具,这些工具不仅能够简化日常操作,还能提升系统的稳定性和安全性。从自动化部署到监控和故障排查,这些工具覆盖了集群管理的各个方面,帮助管理员更好地应对挑战。 ... [详细]
  • 在项目开发过程中,掌握一些关键的Linux命令至关重要。例如,使用 `Ctrl+C` 可以立即终止当前正在执行的命令;通过 `ps -ef | grep ias` 可以查看特定服务的进程信息,包括进程ID(PID)和JVM参数(如内存分配和远程连接端口);而 `netstat -apn | more` 则用于显示网络连接状态,帮助开发者监控和调试网络服务。这些命令不仅提高了开发效率,还能有效解决运行时的各种问题。 ... [详细]
  • 【并发编程】全面解析 Java 内存模型,一篇文章带你彻底掌握
    本文深入解析了 Java 内存模型(JMM),从基础概念到高级特性进行全面讲解,帮助读者彻底掌握 JMM 的核心原理和应用技巧。通过详细分析内存可见性、原子性和有序性等问题,结合实际代码示例,使开发者能够更好地理解和优化多线程并发程序。 ... [详细]
  • 负载均衡基础概念与技术解析
    随着互联网应用的不断扩展,用户流量激增,业务复杂度显著提升,单一服务器已难以应对日益增长的负载需求。负载均衡技术应运而生,通过将请求合理分配到多个服务器,有效提高系统的可用性和响应速度。本文将深入探讨负载均衡的基本概念和技术原理,分析其在现代互联网架构中的重要性及应用场景。 ... [详细]
  • RabbitMQ是使用Erlang编写的一个开源的消息队列,本身支持很多的协议:AMQP,XMPP,SMTP,STOMP,也 ... [详细]
author-avatar
梦-回忆-记忆-梦_429
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有