秒杀/抢购架构设计

news2025/2/5 17:52:09

1 秒杀业务分析

1.1 正常电子商务流程

(1)查询商品;
(2)创建订单;
(3)扣减库存;
(4)更新订单;
(5)付款;
(6)卖家发货

1.2 秒杀业务的特性

(1)低廉价格;
(2)大幅推广;
(3)瞬时售空;
(4)一般是定时上架;
(5)时间短、瞬时并发量高;

秒杀技术挑战

假设某网站秒杀活动只推出一件商品,预计会吸引1万人参加活动,也就说最大并发请求数是10000,秒杀系统需要面对的技术挑战有:

2.1 对现有网站业务造成冲击

秒杀活动只是网站营销的一个附加活动,这个活动具有时间短,并发访问量大的特点,如果和网站原有应用部署在一起,必然会对现有业务造成冲击,稍有不慎可能导致整个网站瘫痪。

解决方案:将秒杀系统独立部署,甚至使用独立域名,使其与网站完全隔离

2.2 高并发下的应用、数据库负载

用户在秒杀开始前,通过不停刷新浏览器页面以保证不会错过秒杀,这些请求如果按照一般的网站应用架构,访问应用服务器、连接数据库,会对应用服务器和数据库服务器造成负载压力。

解决方案:重新设计秒杀商品页面,不使用网站原来的商品详细页面,页面内容静态化,用户请求不需要经过应用服务

2.3 突然增加的网络及服务器带宽

假设商品页面大小200K(主要是商品图片大小),那么需要的网络和服务器带宽是2G(200K×10000),这些网络带宽是因为秒杀活动新增的,超过网站平时使用的带宽。

解决方案:因为秒杀新增的网络带宽,必须和运营商重新购买或者租借。为了减轻网站服务器的压力,需要将秒杀商品页面缓存在CDN,同样需要和CDN服务商临时租借新增的出口带宽

2.4 直接下单

秒杀的游戏规则是到了秒杀才能开始对商品下单购买,在此时间点之前,只能浏览商品信息,不能下单。而下单页面也是一个普通的URL,如果得到这个URL,不用等到秒杀开始就可以下单了。

解决方案:为了避免用户直接访问下单页面URL,需要将改URL动态化,即使秒杀系统的开发者也无法在秒杀开始前访问下单页面的URL。办法是在下单页面URL加入由服务器端生成的随机数作为参数,在秒杀开始的时候才能得到

2.5 如何控制秒杀商品页面购买按钮的点亮

购买按钮只有在秒杀开始的时候才能点亮,在此之前是灰色的。如果该页面是动态生成的,当然可以在服务器端构造响应页面输出,控制该按钮是灰色还 是点亮,但是为了减轻服务器端负载压力,更好地利用CDN、反向代理等性能优化手段,该页面被设计为静态页面,缓存在CDN、反向代理服务器上,甚至用户浏览器上。秒杀开始时,用户刷新页面,请求根本不会到达应用服务器。

解决方案:使用JavaScript脚本控制,在秒杀商品静态页面中加入一个JavaScript文件引用,该JavaScript文件中包含 秒杀开始标志为否;当秒杀开始的时候生成一个新的JavaScript文件(文件名保持不变,只是内容不一样),更新秒杀开始标志为是,加入下单页面的URL及随机数参数(这个随机数只会产生一个,即所有人看到的URL都是同一个,服务器端可以用redis这种分布式缓存服务器来保存随机数),并被用户浏览器加载,控制秒杀商品页面的展示。这个JavaScript文件的加载可以加上随机版本号(例如xx.js?v=32353823),这样就不会被浏览器、CDN和反向代理服务器缓存

2.6 如何只允许第一个提交的订单被发送到订单子系统

由于最终能够成功秒杀到商品的用户只有一个,因此需要在用户提交订单时,检查是否已经有订单提交。如果已经有订单提交成功,则需要更新 JavaScript文件,更新秒杀开始标志为否,购买按钮变灰。事实上,由于最终能够成功提交订单的用户只有一个,为了减轻下单页面服务器的负载压力, 可以控制进入下单页面的入口,只有少数用户能进入下单页面,其他用户直接进入秒杀结束页面。

解决方案:假设下单服务器集群有10台服务器,每台服务器只接受最多10个下单请求。在还没有人提交订单成功之前,如果一台服务器已经有十单了,而有的一单都没处理,可能出现的用户体验不佳的场景是用户第一次点击购买按钮进入已结束页面,再刷新一下页面,有可能被一单都没有处理的服务器处理,进入了填写订单的页面,可以考虑通过cookie的方式来应对,符合一致性原则 。当然可以采用最少连接的负载均衡算法,出现上述情况的概率大大降低。

2.7如何进行下单前置检查

  • 下单服务器检查本机已处理的下单请求数目:
    • 如果超过10条,直接返回已结束页面给用户;
    • 如果未超过10条,则用户可进入填写订单及确认页面;
  • 检查全局已提交订单数目:
    • 已超过秒杀商品总数,返回已结束页面给用户;
    • 未超过秒杀商品总数,提交到子订单系统;

    2.8 秒杀一般是定时上架

该功能实现方式很多。不过目前比较好的方式是:提前设定好商品的上架时间,用户可以在前台看到该商品,但是无法点击“立即购买”的按钮。但是需要考虑的是,有人可以绕过前端的限制,直接通过URL的方式发起购买,这就需要在前台商品页面,以及bug页面到后端的数据库,都要进行时钟同步。越在后端控制,安全性越高。

定时秒杀的话,就要避免卖家在秒杀前对商品做编辑带来的不可预期的影响。这种特殊的变更需要多方面评估。一般禁止编辑,如需变更,可以走数据订正多的流程。

2.9 减库存的操作

有两种选择,一种是拍下减库存另外一种是付款减库存;目前采用的“拍下减库存”的方式,拍下就是一瞬间的事,对用户体验会好些。

  • 库存会带来“超卖”的问题:售出数量多于库存数量

由于库存并发更新的问题,导致在实际库存已经不足的情况下,库存依然在减,导致卖家的商品卖得件数超过秒杀的预期。方案:采用乐观锁

update auction_auctions set
quantity = #inQuantity#
where auction_id = #itemId# and quantity = #dbQuantity#

还有一种方式,会更好些,叫做尝试扣减库存,扣减库存成功才会进行下单逻辑

update auction_auctions set 
quantity = quantity-#count# 
where auction_id = #itemId# and quantity >= #count# 

2.10 秒杀器的应对

秒杀器一般下单个购买及其迅速,根据购买记录可以甄别出一部分。可以通过校验码达到一定的方法,这就要求校验码足够安全,不被破解,采用的方式有:秒杀专用验证码,电视公布验证码,秒杀答题

3 秒杀架构原则

3.1 尽量将请求拦截在系统上游

传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,并发高响应慢,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小【一趟火车其实只有2000张票,200w个人来买,基本没有人能买成功,请求有效率为0】。

3.2 读多写少的常用多使用缓存

这是一个典型的读多写少的应用场景【一趟火车其实只有2000张票,200w个人来买,最多2000个人下单成功,其他人都是查询库存,写比例只有0.1%,读比例占99.9%】,非常适合使用缓存

4 秒杀架构设计

秒杀系统为秒杀而设计,不同于一般的网购行为,参与秒杀活动的用户更关心的是如何能快速刷新商品页面,在秒杀开始的时候抢先进入下单页面,而不是商品详情等用户体验细节,因此秒杀系统的页面设计应尽可能简单。

商品页面中的购买按钮只有在秒杀活动开始的时候才变亮,在此之前及秒杀商品卖出后,该按钮都是灰色的,不可以点击。

下单表单也尽可能简单,购买数量只能是一个且不可以修改,送货地址和付款方式都使用用户默认设置,没有默认也可以不填,允许等订单提交后修改;只有第一个提交的订单发送给网站的订单子系统,其余用户提交订单后只能看到秒杀结束页面。

要做一个这样的秒杀系统,业务会分为两个阶段,第一个阶段是秒杀开始前某个时间到秒杀开始, 这个阶段可以称之为准备阶段,用户在准备阶段等待秒杀;第二个阶段就是秒杀开始到所有参与秒杀的用户获得秒杀结果, 这个就称为秒杀阶段

4.1 前端层设计

首先要有一个展示秒杀商品的页面, 在这个页面上做一个秒杀活动开始的倒计时, 在准备阶段内用户会陆续打开这个秒杀的页面, 并且可能不停的刷新页面。这里需要考虑两个问题:

(1) 第一个是秒杀页面的展示

我们知道一个html页面还是比较大的,即使做了压缩,http头和内容的大小也可能高达数十K,加上其他的css, js,图片等资源,如果同时有几千万人参与一个商品的抢购,一般机房带宽也就只有1G~10G,网络带宽就极有可能成为瓶颈,所以这个页面上各类静态资源首先应分开存放,然后放到cdn节点上分散压力,由于CDN节点遍布全国各地,能缓冲掉绝大部分的压力,而且还比机房带宽便宜。

(2) 第二个是倒计时

出于性能原因这个一般由js调用客户端本地时间,就有可能出现客户端时钟与服务器时钟不一致,另外服务器之间也是有可能出现时钟不一致。客户端与服务器时钟不一致可以采用客户端定时和服务器同步时间,这里考虑一下性能问题,用于同步时间的接口由于不涉及到后端逻辑,只需要将当前web服务器的时间发送给客户端就可以了,因此速度很快,就我以前测试的结果来看,一台标准的web服务器2W+QPS不会有问题,如果100W人同时刷,100W QPS也只需要50台web,一台硬件LB就可以了~,并且web服务器群是可以很容易的横向扩展的(LB+DNS轮询),这个接口可以只返回一小段json格式的数据,而且可以优化一下减少不必要cookie和其他http头的信息,所以数据量不会很大,一般来说网络不会成为瓶颈,即使成为瓶颈也可以考虑多机房专线连通,加智能DNS的解决方案;web服务器之间时间不同步可以采用统一时间服务器的方式,比如每隔1分钟所有参与秒杀活动的web服务器就与时间服务器做一次时间同步

(3) 浏览器层请求拦截
  • 产品层面:用户点击“查询”或者“购票”后,按钮置灰,禁止用户重复提交请求;
  • JS层面:限制用户在x秒之内只能提交一次请求;

4.2 站点层设计

前端层的请求拦截,只能拦住小白用户(不过这是99%的用户),高端的程序员根本不吃这一套,写个for循环+线程池,直接调用你后端的http请求,怎么办呢?

  • 同一个uid,限制访问频度,做页面缓存,x秒内到达站点层的请求,均返回同一页面。
  • 同一个item的查询,例如手机车次,做页面缓存,x秒内到达站点层的请求,均返回同一页面。
    如此限流,又有99%的流量会被拦截在站点层。

4.2 服务层设计

站点层的请求拦截,只能拦住普通程序员,高级黑客,假设他控制了10w台肉鸡(并且假设买票不需要实名认证),这下uid的限制不行了吧?怎么整?
(1)通过服务层,清楚的知道华为只有1万部手机,清楚的知道一列火车只有2000张车票,我透10w个请求去数据库有什么意义呢?对于写请求,做MQ削峰填谷,每次只透过有限的写请求去数据层,如果均成功再放下一批,如果库存不够则队列里的写请求全部返回“已售完”
(2)对于读请求,还用说么?cache来抗,不管是memcached还是redis,单机抗个每秒10w应该都是没什么问题的,又或者数据静态化、二级缓存等等;
如此限流,只有非常少的写请求,和非常少的读缓存mis的请求会透到数据层去,又有99.9%的请求被拦住了。

总体而言:
拦截无效请求
分而治之(负载、区域划分、中心划分、服务解耦、...)
缓存
MQ(削峰填谷/解耦/异步)
预处理
批处理

4.4 数据库设计

4.4.1 基础概念

概念一 “单库”

在这里插入图片描述

概念二 “分库分表/分片”

在这里插入图片描述
分片解决的是“数据量太大”的问题,也就是通常说的“水平切分”。一旦引入分片,势必有“数据路由”的概念,哪个数据访问哪个库。路由规则通常有3种方法:

  • 范围:range
    • 优点:简单,容易扩展
    • 缺点:各库压力不均(新号段更活跃)
  • 哈希:hash
    大部分互联网公司采用的方案二:哈希分库,哈希路由
    • 优点:简单,数据均衡,负载均匀
    • 缺点:迁移麻烦(2库扩3库数据要迁移)
  • 路由服务:router-config-server
    • 优点:灵活性强,业务与路由算法解耦
    • 缺点:每次访问数据库前多一次查询
概念三“分组”

在这里插入图片描述
分组解决“可用性”问题,分组通常通过主从复制的方式实现。
互联网公司数据库实际软件架构是:又分片,又分组

设计思路

数据库软件架构师平时设计些什么东西呢?至少要考虑以下四点:

  • 如何保证数据可用性;
  • 如何提高数据库读性能(大部分应用读多写少,读会先成为瓶颈);
  • 如何保证一致性;
  • 如何提高扩展性;

总结

  • 从业务到技术,层层拦截,过滤请求
  • 分而治之(负载、区域划分、中心划分、服务解耦、…)
  • 缓存
  • MQ(削峰填谷/解耦/异步)
  • 预处理
  • 批处理
  • 数据库合理的分库分表和主从结构

参考资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/103131.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Odoo丨手把手教你灵活控制表单明细的创建与删除!

文章目录一、前言二、表单明细的创建与删除1.根据主表字段带出明细信息2.根据主表字段控制明细表的添加和删除功能三、总结一、前言 我们知道,在Odoo页面上,控制的最小单位是字段,可通过Odoo提供的属性,控制字段的必填、只读、隐…

Spring Cloud 应用 Proxyless Mesh 模式探索与实践

作者:十眠 Service Mesh 简介 Service Mesh 早已不是一个新兴的概念,目前已经有许多关于 Service Mesh 的探索以及实践。 2016 年可以说是 Service Mesh 的元年,Buoyant 公司 CEO William Morgan 率先发布 Linkerd ,成为业界首…

Java中的多线程(上)

作者:~小明学编程 文章专栏:JavaEE 格言:热爱编程的,终将被编程所厚爱。 目录 多线程 什么是线程 为什么需要多线程 进程和线程的区别(面试重点) Java实现多线程 多线程带来的好处 认识Thread类 T…

前端基础_贝塞尔和二次方曲线

贝塞尔和二次方曲线 贝塞尔曲线可以是二次和三次方的形式,常用于绘制复杂而有规律的形状。 绘制贝塞尔曲线主要使用bezierCurveTo方法。该方法可以说是lineTo的曲线版,将从当前坐标点到指定坐标点中间的贝塞尔曲线追加到路径中。该方法的定义如下。 b…

艾美捷耗氧率检测试剂盒说明书及相关研究

细胞内稳态通过ATP的产生来维持。ATP的生成可以通过单独的糖酵解(无氧呼吸)或通过糖酵解与氧化磷酸化的耦合来完成。氧化磷酸化是氧(O2)依赖性的,发生在线粒体中,是哺乳动物细胞合成ATP的最有效和优选的方法…

电力系统可视化——比PowerWorld还精彩(Matlab实现)

目录 1 概述 2 PowerWorld 3 案例及Matlab代码实现 3.1 案例 3. 2 结果展现 3.3 Matlab代码实现 1 概述 信息可视化的应用为电力行业带来了巨大的希望,但其潜力尚未被可视化社区充分挖掘。先前关于电力系统可视化的工作仅限于在地理布局之上描绘原始或处理过的…

【数据结构】单链表OJ

目录 一、反转单链表 描述 二、返回链表的中间节点 三、返回倒数第K个节点 描述 示例1 四、合并两个已排序的链表 描述 五、分隔链表 六、判断一个链表是否为回文结构 描述 示例1 示例2 示例3 七、两个链表的第一个公共结点 描述 输入描述: 返回值描…

LICEcap:GIF屏幕录制工具

写博客的时候,我经常会用到需要自制gif的场景,我之前一直使用视频转gif工具处理https://tool.lu/video2gif/,大致就是利用qq录屏成视频,然后通过工具转成gif。 今天无意间看到其他博主用了 LICEcap 工具 LICEcap 是一款简洁易用的…

“隐形贫困者”自学Python做副业,教你如何月入10000+

现在的年轻人总是觉得攒不下钱,虽然表面风光,每天出入高级CBD,可是几乎每个人都是月光族,这一类人被定义为“隐形贫困者”,原因是什么呢?根据小编的分析,现在人们对生活质量要求普遍提高了&…

毕业设计 单片机智能避障超声波跟随小车 - 物联网 嵌入式

文章目录0 前言1 项目背景2 实现效果3 设计原理HC-SR04超声波模块5 部分代码6 最后0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告…

玩转云端 | 数据管理深似海,运维如何变“路人”?

数字技术正在改变人们生产、生活的方方面面。作为数字经济时代的重要生产要素,数据正以指数级别爆发式增长,企业对于数据存储及处理的方式和要求已是“今时不同往日”。最早古人存储数据的方法是“以物记物”或“借物记物”,即用更易于携带的…

如何视频裁剪?建议收藏这几种裁剪视频的方法

现在的网络很是方便,我们可以轻轻松松的在网上找到各种网课视频来进行学习。不过有些网课里面的重点内容不仅比较少,还很分散,让我们很难做到高效的学习。但其实我们可以通过视频裁剪,将有重点内容的视频裁剪下来,这样…

SpringBoot SSMP案例整合二 运维篇

目录 一、程序打包 二、项目打包后 若启动失败 三、虚拟机启动SpringBoot项目(Linux) 四、临时属性 五、配置文件4级分类 六、自定义配置文件 七、多环境开发(yml版本) 八、多环境开发多文件版(yml版&#xf…

C++ Primer 第四章 表达式

C Primer 第四章 表达式4.1. Fundamentals4.1.1. Lvalues and Rvalues4.1.2. Precedence and Associativity4.2. Arithmetic Operators4.4. Assignment Operators4.5. Increment and Decrement Operators4.9. The sizeof Operator4.10. Comma Operator4.11. Type Conversions4.…

中国风?古典系?AI中文绘图创作尝鲜!⛵

💡 作者:韩信子ShowMeAI 📘 深度学习实战系列:https://www.showmeai.tech/tutorials/42 📘 本文地址:https://www.showmeai.tech/article-detail/413 📢 声明:版权所有,转…

Java——B-树

概念 当我们使用avl树或者红黑树进行数据检索时,虽然树是平衡的,可以保证搜索的效率大概是logN。但是当我们的数据量比较大时,只能在内存中存储数据在硬盘中的指针,这时如果我们要检索数据,最少也需要比较树的高度次。…

【算法】单词接龙,合并区间, 二叉搜索树的最近公共祖先,旋转排序数组中的最小值看看有你会的吗?

算法学习有些时候是枯燥的,每天学习一点点 算法题目一. 单词接龙 II 题目描述java 解答参考二. 合并区间 题目描述java 解答参考三. 二叉搜索树的最近公共祖先 题目要求java实现方案四 寻找旋转排序数组中的最小值一. 单词接龙 II 题目描述 按字典 wordList 完成从…

Azure RTOS 嵌入式无线网络框架简化物联网应用开发

一、Azure RTOS概述 Azure RTOS 是一个实时操作系统 (RTOS),适用于由微控制器 (MCU) 提供支持的物联网 (IoT) 和边缘设备, Azure RTOS 旨在支持高度受限设备(电池供电,并且闪存容量不到 64 KB)。简而言之,…

某HR分享:2n和n+3的基数不一样,n+3比2n拿得多!仲裁期间不能入职新公司,千万别轻易仲裁,得不偿失!...

被裁员时,要2n还是要n3?是否选择仲裁?一位hr说,跟走过仲裁的同学和律师朋友打听了下,原来2n和n3完全不一样。n3的n取的是“非私营单位从业人员平均工资”的三倍,杭州市是3.2万。2n的n取的是“全社会就业人员…

Android设计模式详解之单例模式

前言 定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 使用场景:确保某个类有且仅有一个对象的场景,避免产生多个对象消耗过多的资源。比如要访问IO和数据库资源,应该考虑使用单例模式。 …