案例背景

在大促活动期间,“预约抢购”已经是各大电商平台的主要促销手段,京东自然也会和一些大的供应商合作,推出一些低价的爆款产品,比如 2020 年的 “1499 元抢购飞天茅台”活动,就让很多人每天准时准点拿着手机拼人品

先把需求梳理一下,总的来说,实现一个抢购系统大概可以分为四个阶段

  • 商品预约:用户进入商品详情页面,获取购买资格,并等待商品抢购倒计时
  • 等待抢购:等待商品抢购倒计时,直到商品开放抢购
  • 商品抢购:商品抢购倒计时结束,用户提交抢购订单,排队等待抢购结果,抢购成功后,扣减系统库存,生成抢购订单
  • 订单支付:等待用户支付成功后,系统更新订单状态,通知用户购买成功

商品预约阶段

这几年,很多电商平台为了方便流量运营,改造了传统秒杀场景,通过先预约再抢购的方式预热商品,并根据预约量调整运营策略。而且在预约抢购的活动中,为了增加商品售卖量,会允许抢购前,预约资格超过实际的库存数量

问题来了:如何在高并发量的情况下,让每个用户都能得到抢购资格呢?可以利用锁的实现原理,来控制抢购资格的发放

基于 Redis 实现分布式锁(这是最常用的方式),在加锁的过程中,实际上是给 Key 键设置一个值,为避免死锁,还要给 Key 键设置一个过期时间

SET lock_key unique_value NX PX 10000

  • lock_key 就是 key 键;
  • unique_value 是客户端生成的唯一的标识;
  • NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
  • PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁

解锁的过程就是将 lock_key 键删除,但不能乱删,要保证执行操作的客户端就是加锁的客户端。而这个时候, unique_value 的作用就体现出来,可以通过 Lua 脚本判断 unique_value 是否为加锁客户端

选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,保证了锁释放操作的原子性

// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

这样一来,就通过使用 SET 命令和 Lua 脚本在 Redis 单节点上完成了分布式锁的加锁和解锁。但你要注意,此方案是基于单节点的 Redis 实例实现的,如果此时 Redis 实例发生故障宕机,那么锁变量就没有了,客户端也就无法进行锁操作,就会影响到业务的正常执行。所以,基于 Redis 实现分布式锁时,你还要掌握如何保证锁的可靠性,也就是怎么基于多个 Redis 节点实现分布式锁

等待抢购阶段

用户预约成功之后,在商品详情页面中,会存在一个抢购倒计时,这个倒计时的初始时间是从服务端获取的,用户点击购买按钮时,系统还会去服务端验证是否已经到了抢购时间,在等待抢购阶段,流量突增,因为在抢购商品之前(尤其是临近开始抢购之前的一分钟内),大部分用户会频繁刷新商品详情页,商品详情页面的读请求量剧增, 如果商品详情页面没有做好流量控制,就容易成为整个预约抢购系统中的性能瓶颈点

问题来了:如何解决等待抢购时间内的流量突增问题呢?有两个解决思路

  • 页面静态化:提前对抢购商品的详情页面做静态化,生成一个静态页面,再把页面放到距离用户最近的 CDN 节点中,这样一来,当浏览器访问页面时,就会自动缓存该页面的静态资源文件(对于静态化技术,很多页面端的模板引擎都支持这样的功能)
  • 服务端限流:对商品详情页中的动态请求接口设置最大并发访问数量(具体的数量根据上线前的性能压测为准),防止超出预期的请求集中访问系统,造成系统压力过载。操作上,你可以在商品详情页的后端系统入口层(如网关系统)中进行接口限流,如果使用 Nginx 来做反向代理,可以直接基于 Nginx 配置限流算法,比如 Nginx 的 ngx_http_limit_req_module(限制单位时间内所有 IP 的请求数量)和 ngx_stream_limit_conn_module(限制单位时间内单个 IP 的请求数量)两个模块就提供了限流控制的功能,所以还要提前掌握限流策略的原理,如令牌桶算法的原理

商品抢购阶段

在商品抢购阶段,用户会点击提交订单,这时,抢购系统会先校验库存,当库存足够时,系统会先扣减库存,然后再生成订单。在这个过程中,短时间之内提交订单的写流量非常高,所以为了做流量削峰,会将提单请求暂存在消息队列中,并提示用户“抢购排队中……”然后再由后端服务异步处理用户的请求

可以基于数据库和缓存两种方式,来实现校验库存和扣减库存的操作

因为抢购场景的瞬时流量极高,一般不会直接基于数据库来实现(因为每次操作数据库,即使通过消息队列做了流量削峰,对数据库来说压力也很大,会产生性能瓶颈)。如果非要基于数据库的话,你要通过分布式锁来优化扣减库存的并发操作,但此阶段的分布式锁对可靠性的要求会极高(因为在大促抢购阶段,小的可用性故障,都可能造成大的线上事故),所以基于单节点 Redis 实现的分布式锁不合适,你要选择多节点 Redis 实现分布式锁,或者选型 ZooKeeper

为了避免上述问题,一般基于缓存来存储库存,实现扣减库存的操作。这样在提交订单时,库存的查询和锁定就不会给数据库带来性能瓶颈。不过仍要注意,基于缓存(如 Redis)的库存扣减操作,仍要考虑缓存系统的单点问题,就算是多节点存储库存,也要引入锁的策略,保证 Redis 实现库存的一致性

实现了校验库存和扣减库存之后,最后一步是生成抢购订单。由于数据库表会承载订单数据,一旦出现瞬时流量,磁盘 I/O、数据库请求连接数等资源都会出现性能瓶颈,你可以考虑对订单表分库分表,通过对用户 ID 字段进行 Hash 取模,实现分库分表,提高系统的并发能力

从“商品抢购阶段的架构设计”中可以总结出三个技术考点:流量削峰、扣减库存、分库分表

  • “流量削峰”的考点

流量削峰是由于正式抢购场景下,短时间内的提单请求非常高,所以引入消息队列做异步化,然后在抢购系统的后端服务中,启动若干个队列处理消息队列中的提单请求,再执行校验库存、下单等逻辑

  • “扣减库存”的考点

当基于 Redis 实现库存的扣减时,要考虑怎么解决 Redis 的单点问题。而如果基于 Redis 集群来实现扣减库存,还要解决 Redis 在哨兵模式部署的情况下,因为主从切换带来的数据不一致的问题

  • “分库分表”的考点

还有一个容易忽略的问题:带宽的影响。由于抢购入口的请求量会非常大,可能会占用大量带宽,为了不影响提交订单的请求,有时会从网络工程的角度解决,通过单独的子域名绑定独立的网络服务器,这里就会涉及 DNS 的设计与优化手段

订单支付阶段

在用户支付订单完成之后,一般会由支付平台回调系统接口,更新订单状态。在支付回调成功之后,抢购系统还会通过异步通知的方式,实现订单更新之外的非核心业务处理,比如积分累计、短信通知等,此阶段可以基于 MQ 实现业务的异步操作

订单支付后操作

针对服务的异常(如宕机),会存在请求数据丢失的可能,比如当支付回调系统后,修改订单状态成功了,但是在异步通知积分系统,更新用户累计积分时,订单系统挂掉了,此时 MQ 还没有收到这条消息,那么这条消息数据就无法还原了

订单支付后操作(异常)

还要考虑 可靠消息投递机制:先做消息的本地存储,再通过异步重试机制,来实现消息的补偿。比如当支付平台回调订单系统,然后在更新状态的同时,插入一个消息,之后再返回第三方支付操作成功的结果。最后,通过数据库中的这条消息,再异步推送其他系统,完成后续的工作

订单支付后操作(新方案)

总结

商品预约阶段:要掌握如何在高并发的场景下通过锁的方式,让每一个用户都获取到抢购资格,结合业务场景对于并发控制的需求诉求和成本的考虑,在商品预约阶段,你可以基于 Redis 来实现分布式锁

等待抢购阶段:此阶段对页面的查询请求会很高,尤其是临近抢购倒计时的流量突增,解决方案是做页面静态化和服务端限流

商品抢购阶段:商品抢购是整个流程中涉及技术点最多的阶段,瞬时流量会带来极大的压力,所以通过 MQ 做了同步转异步,实现对流量的削峰,从而让请求排队等待,然后有序且有限地进入到后端服务,而你必须掌握消息队列的丢失、重复和积压问题的解决方案;另外在扣减库存的时候,为了解决扣减存储不超售的问题,同样还需要引入锁的机制

订单支付阶段:在用户支付完成后,系统通常还需要处理一些非核心操作,你可以通过 MQ 通知的方式来实现系统间的解耦和异步通信,但依旧要保证消息的可靠性(当然也可以通过 RPC 同步调用的方式来实现),所以你也要掌握 RPC 和 MQ 的相关知识点

如何让系统抗住双十一的预约抢购活动?相关推荐

  1. 9.如何抗住双11预约抢购活动

    案例背景 在大促活动期间,"预约抢购"已经是各大电商平台的主要促销手段,京东自然也会和一些大的供应商合作,推出一些低价的爆款产品,比如 2019 年的 "1499 元抢购 ...

  2. 【商品架构day3】京东商品系统的演进之路 - 如何抗住亿级流量

    本文来自京东尤凤凯老师的分享.商品,黄金交易流程最基础.最核心的环节,无商品不电商.商品数据无处不在,商家(采销.供应商)发布管理.供应商下采购单.仓储配送.促销.搜索.商详页展现.购物支付.财务结算 ...

  3. 超千万人同时在线,抖音快手,是怎么抗住高并发?

    不得说现在直播真火,以一个程序员的角度来看,我关注的可不是那些女主播 我也听不懂你们说的什么乌鸡哥,毕竟我只是一个纯绿色程序员. 我关注的是直播系统的架构设计,高稳定.高可用.低延迟是一款直播弹幕系统 ...

  4. 用Go重构C语言系统,这个抗住春晚红包的百度转发引擎承接了万亿流量

    整理 | 夕颜 出品 | AI科技大本营(ID:rgznai100) 11 月 20 日,百度的万亿流量转发引擎 BFE 登上了 GitHub Trending Top 3,今日 Star 已突破 2 ...

  5. SpringCloud注册中心集群化及如何抗住大型系统的高并发访问

    一.场景引入 本人所在的项目由于直接面向消费者,迭代周期迅速,所以服务端框架一直采用Springboot+dubbo的组合模式,每个服务由service模块+web模块构成,service模块通过公司 ...

  6. 用 Go 重构 C 语言系统,这个抗住春晚红包的百度转发引擎承接了万亿流量

    整理 | 夕颜 出品 | AI科技大本营(ID:rgznai100) 11 月 20 日,百度的万亿流量转发引擎 BFE 登上了 GitHub Trending Top 3,今日 Star 已突破 2 ...

  7. 每次面试都会问我:你们系统有多大QPS,怎么抗住的?

    V-xin:ruyuanhadeng获得600+页原创精品文章汇总PDF 目录 1.尴尬的面试现场:第一幕 2.尴尬的面试现场:第二幕 3.别让你学的技术成为空中楼阁 4.想方设法的 "虐虐 ...

  8. 抗住 8 亿人买买买!双 11 背后黑科技大曝光

    作者 | 马超 责编 | 伍杏玲 出品 | CSDN(ID:CSDNnews) "双 11"."618"等活动已由原来单纯电商促销变成经济增长的引擎,今年&qu ...

  9. 阿里巴巴为什么能抗住90秒100亿?看完这篇你就明白了!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:huashiou 链接:https://segmentfau ...

最新文章

  1. iis出现 Server Application Error 错误解决方法(xp iis5.1 配置asp项目出现500错)
  2. ASP.NET Core Web服务器 Kestrel和Http.sys 特性详解
  3. 计算机操作系统(8):进程的控制
  4. 中国象棋源码c语言,中国象棋C语言源代码.doc
  5. Vue2学习小记-给Vue2路由导航钩子和axios拦截器做个封装 1
  6. java的if判读_java if判断
  7. 集成融云没有ipc进程的天坑
  8. mysql如何导入mdl文件_将sql文件导入PowerDesigner中的方法(将oracle sql文件转换成mysql)...
  9. MATLAB希尔伯特变换
  10. 企业微信怎么拉黑好友?
  11. 网站服务器历史解析记录查询,域名解析ip历史查询
  12. ios保存gif到相册_iOS如何保存下载GIF图片
  13. 超短波视距通信极限距离计算公式
  14. 无线耳机除了苹果哪个牌子好?类似苹果耳机的蓝牙耳机推荐
  15. 生存智慧——新的生活方式
  16. js取整,保留小数位数、四舍五入、科学记数法及去掉数字末尾多余的0
  17. 信用贷款违约预测项目
  18. 巴西龟饲养日志-----黑壳虾
  19. 【电气设计】理论知识学习(持续更新中...)
  20. EEPROM存储芯片24C02

热门文章

  1. C#关于64位双精度浮点数Double(DReal)一步步按位Bit进行解析
  2. 菜鸟也疯狂!8分钟用Python做一个酷炫的家庭随手记
  3. 一体化污水处理设备的三种污水处理工艺详解
  4. 院校护理实验室污水处理设备
  5. 黑马程序员-MyBatis笔记
  6. 计算机技术与软件资格证书有哪些,请问计算机技术与软件专业技术资格的证书有什么用..._出版资格_帮考网...
  7. 干货 | 最新Windows事件查看器.NET反序列化漏洞分析
  8. OpenCV—python 简单的图像质量检测
  9. Linux-分屏软件terminator的安装配置
  10. 智能设计(智能家居的研发实战实操)专项技能培训通知