文章目录
- 电商平台-“秒杀”系统技术方案
- 前言
- 一、什么是秒杀?
- 二、秒杀的技术难点?
- 1.避免对现有网站的冲击
- 2.高效解决网络带宽压力
- 3.高效的处理高并发下后端系统的压力
- 3.1 限流
- 3.2 过载保护
- 3.3 改进业务流程并高效利用缓存
- 4 防止秒杀活动开始前下单
- 5 防止作蔽
- 6 防止超卖
- 6.1 解决思路1:悲观锁
- 6.2 解决思路2:FIFO队列
- 6.3 解决思路3:乐观锁
- 三、总结
前言
本文主要讲解的是电商平台中“秒杀”系统的技术难点及解决方案。
一、什么是秒杀?
秒杀,是以卖家发布超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟的时间。由于这种销售方式给电商系统带来很多技术难题,如:访问的用户突然爆增,系统不堪重负,死机,系统无法访问;超卖,原本只卖100件商品,但现在卖了200件商品;作弊导致一个用户购买了多件商品等问题。现第二章我们将详细的讲解发现这些问题的原因及解决方案。
秒杀主要的应用场景包括:预约抢购、限时购、春运12306抢购火车票、四川某西医院专家号等都属手秒杀范畴。
二、秒杀的技术难点?
以下我将从这6个方面详细的讲解技术难度及解决方案。
1.避免对现有网站的冲击
对电商而言,秒杀一般是针对某些特定商品开展的活动,它的开展不应该影响原有系统业务的正常运营。在秒杀进行时,对网络和系统会带来巨大的压力,处理不好会对整个电商网站带来致命的影响。因此秒杀系统应该与原业务平台进行分离,避免对整个电商平台造成影响。解决的技术方案:独立部署秒杀系统,秒杀结束后,回收资源。
2.高效解决网络带宽压力
问题描述:
因为秒杀在瞬间有大量的用户访问秒杀系统的相应页面,尤其是商品页面。所以系统对带宽要求非常大。如果网络带宽不足,导致用户端无法正常显示商品信息,给用户带来不好的体验。解决这个问题不能简单的增加网络带宽。解决的方案主要有2个。
解决方案:1.采用商品页面静态化
不使用网站原来的商品页面,而是把页面内容(商品描述,参数等)全部写入一静态页面,这样用户请求无需经过应用服务器的业务逻辑处理,也不需要访问数据库。所以秒杀商品服务不需要部署动态的Web服务器、数据库服务器。
解决方案:2.采用CDN缓存商品页面,并增加带宽
采用CDN缓存商品页面,同时需要和CDN服务商临时租借新增的出口带宽。
3.高效的处理高并发下后端系统的压力
问题描述:
用户在秒杀开始前,通过不停的刷新浏览器页面保证不错过秒杀,会对服务器负载产生极大的压力
整体解决方案设计原则:限流、过载保护、改善业务流程、使用缓存
3.1 限流
问题描述:
一般情形下,请求都放在后端处理,但在秒杀情景下,数据读写锁冲突会非常严重,并发高响应慢,几乎所有请求都超时,虽然流量大,但是下单成功的有效流量甚少。由于访问量巨大,支后端平台造成影响,严重时还可能导致云平台宕机,同时客户访问慢,影响客户体验。
解决方案:
基于“限流”的思路,在每个环节设置“限流阀门”,层层过滤到后端的请求,把请求尽可能压缩在前端。
以下是每个阶段的限流数:x>y>z
以下是限流的流程图说明:
3.2 过载保护
问题描述:
在秒杀情景下,并发请求量可能相当大,当超过系统吞吐量后会导致系统崩溃。当服务器A的访问达到“崩溃点”时,A服务器系统崩溃,原来访问A的请求都会转发到其他服务器;再次导致其他服务器访问量过大,致使整个系统崩溃。
系统瘫痪常见的修复方法:
重启服务
对于“秒杀”场景,重启服务不能解决问题,原因如下:
1.内存数据库的启动要较长时间的预热 。
2.越是秒杀活动,用户点击行为越频繁,而且是系统越不响应,用户点击发送请求越频繁。
假设每个服务每秒正常请求数是A,因为每秒并发请求达到B而导致“雪崩”,则当它重启时,所有请求转到基他服务器C (并且C一定大于B), 所以其他服务器也会崩掉,最终导致整个系统瘫痪。
秒杀和抢购的应用场景,流量往往是超乎我们想象。如果检测到系统满负载状态,拒绝请求也是一种很好的保护措施。
解决方案:
启用“过载保护”,当请求范围超过其合理访问,则把这些请求拒绝掉,从而让其不会让系统崩盘。
3.3 改进业务流程并高效利用缓存
可以通过改进业务流程来提升系统的稳定性,提高用户体验。业务流程的改进应遵守的原则:“及时反馈”优于 “滞后反馈”
及时反馈:利用缓存当场告诉用户秒杀结果,之后异步写入数据库。
滞后反馈:当场不告诉用户秒杀结果,过一段时间后写入数据库,再告知用户成功或失败。(用户体验不好)
解决方案:
下单页面屏蔽收货地址、发票等各种额外信息,带下单完成后再补填。
4 防止秒杀活动开始前下单
问题描述:
秒杀活动有精确的开始和结束时间,只有规定时间内才可以下单,这样对所有人是公平的,在此之前只能浏览商品不能下单。有些人可能通过直接获取秒杀的商品URL页面,在秒杀活动开始之前就下单。
解决方案:
页面上JS控制“下单”按钮的启用,下单链接采用动态URL
- 在秒杀商品静态页面上加入一个JS文件引用,该JS文件中加入是否开始的标志和URL的随机数,当秒杀活动开始时才生成这个新的JS文件被用户浏览器加载,该文件采用随机版本号,因此不会被浏览器/CDN/反向代理服务器缓存。
- 在下单页面的URL中加入由上步骤JS文件中带的URL随机数作为参数。
5 防止作蔽
问题描述:
正常来说,一个人只有一次抢购商品的机会,但是有人会用技术手段来进行作弊,抢购更多商品。
作弊的本质是通过各种技术手段发送更多的请求,来获得更多的机会参与秒杀活动。
具体典型的几种作弊方式:
- 同一账号,一次性发多个请求的防范
作弊方式:
一些浏览器脚本或插件,一次性发送多个请求到下单服务。
防范方式:
在程序入口,一个账户只能接受一个请求,其他请求被过滤。一个账号只允许接受1个请求,其他请求过滤。不仅解决了同一个账号,发送N个请求的问题,还保证了后续的逻辑流程的安全
- 多个僵尸账号同IP发送请求的防范
作弊方式1:
注册多个“僵尸”账号,将其混入到普通账号中参与秒杀活动,占据其他正常账号的机会。
防范方式:
a. 因为这些“僵尸”账号多半从一个局域网发出,所以需限制高频度IP
b. 为了避免局域网中对多个人同时进行秒杀活动的“误伤”操作,在下单时识别出真实账号和僵尸账号,通过做一些需要”AI“的题目来进行前端限制。
解决方案:
1.下单时输入随机数字,该随机数字由ImageIO画出,后台对该数字的正确性做校验
2.做一些图形识别,后端对识别的正确性做校验 (防范效果最好)
3.做一些需要思考的简单选择题,后端对正确性校验
作弊方式2:
注册多个僵尸账号,使用独立的IP进行发送这些支付请求。从而使得这些账号绕过上个场景的“IP监控”关。
解决方案:
1.提高业务门槛。例如:限制参与秒杀的账号等级。
2.通过账号行为的”数据挖掘“来提前清理掉僵尸号。
6 防止超卖
超卖场景:
假设秒杀有100个商品,在最后一刻,已经销售了90个商品,仅剩最后10个商品。这个时候,有50个并发请求,这批请求读取到的商品余量都是10个,然后都通过了这一个余量判断,都下单成功,但系统却超卖了40个,最终导致超卖。
超卖实质:在高并发情况下的库存处理不当
6.1 解决思路1:悲观锁
解决思路:
在修改数据的时候,采用锁定状态,排斥外部请求的修改,遇到加锁的状态,就必须等待。
缺陷:
上述方案的解决超卖的问题,但是由于秒杀是“高并发”,会导致每个请求都需要等待“锁”释放,有些请求可能无法获取锁,导致请求等待或失败。同时,这种请求会很多,瞬间增大系统的平均响应时间,结果是可用连接数被耗尽,系统陷入异常。
6.2 解决思路2:FIFO队列
解决思路:
将请求放入队列中,采用FIFO(First Input First Output,先进先出)
缺陷:
上述方案解决了锁的问题,全部请求采用“先进先出”的队列方式来处理。但高并发的场景下,因为请求很多,很可能一瞬间将队列内存“撑爆”,然后系统又陷入到了异常状态。或者设计一个极大的内存队列,也是一种方案,但是,系统处理完一个队列内请求的速度根本无法和疯狂涌入队列中的数目相比。也就是说,队列内的请求会越积累越多,最终Web系统平均响应时候还是会大幅下降,系统还是陷入异常。(进多出少)
6.3 解决思路3:乐观锁
减库存操作都加上一条减后检查条件,保证减完后不能为负数,否则下单不成功!
update goods_stock set quantity=quantity-sell where (quantity -sell ) >= 0 and goods_id=xxxx
此解决思路是防止超卖的最优解决方案。