秒杀系统设计方案
# 1. 什么是秒杀系统?
秒杀系统是我们电商系统中常见的一种业务模式,用于吸引用户,刺激留存及消费所做的一种活动。 秒杀系统的例子:整点秒杀、商品秒杀。
秒杀系统的特点:
- 瞬时流量极大,过了秒杀时间点流量结束,所以不能用机器堆 qps
- 秒杀商品库存极少,例如 1w 用户去抢 10 瓶茅台
- 秒杀时间点未到的时候,刷新量极大,静态资源被访问剧增
秒杀系统的一般流程:
- 运营策划秒杀活动,选品,在秒杀中台建立秒杀活动,需要距秒杀开始提前一段时间。同时将秒杀数据、库存信息写入缓存(由缓存进行交互而不是 MySQL)
- 业务方研发根据实际情况来看自身机器是否可以扛得住。(一般在大公司都是由一个秒杀中台来控制各种秒杀,业务方只需要跟他去做一些数据对接,从而减少业务方的流程)
- 用户可以进行正常的秒杀。
# 2. 秒杀系统设计思路点
针对于以上秒杀系统的特点,我们映射到技术层面上,就有如下一些需要考虑的点:
- 高并发,快响应:秒杀一般是 C 端产品吸引用户的点,需要考虑用户体验。并发量极高,对服务有要求,要抗住并发。
- 防止超卖:秒杀的商品一般数量有限且较为精品,不能因为大用户量高并发扣减库存,导致商品超卖,库存变负。
- 防刷子:防止机器人恶意抢占订单,导致正常用户抢不到商品。
- 页面资源访问多:需要考虑静态化、CDN、静态资源缓存及压缩。
- 前端秒杀按钮:通过前端按钮限制一些请求打到后端,比如按钮置灰。
- 秒杀真连接隐藏:防止后台交互链接,非秒杀期间不能漏出。
- 异步处理订单后续:秒杀成功后,订单的处理采用异步方式,不要同步地去返回。
- 订单失败补偿:因为用户一旦秒杀成功以后,订单一定要成功,如果遇到订单后续处理失败,必须配套有后续的补偿措施来保证这个订单成功。
- 服务降级:遇到紧急情况,可以快速处理。
# 2.1 高并发快响应
针对于请求和用户的高并发,我们需要在上线之前,首先要做好压测,知道服务 qps 瓶颈点。针对我们的日常业务秒杀的预估,进行服务器的准备。测量 tps,寻找薄弱点进行代码优化,服务支撑优化。
一般采用 redis + 热数据 + mq。前端辅以静态化、cdn 加速。带宽扩展,集群负载均衡。整体这么一套方式就可以达到高并发和快速响应。
# 2.2 防止超卖
我们一般为了速度的快,我们要在秒杀活动建立之前就把库存同步到 redis 中。在真正秒杀的时候,我们一般分为两步:
- 第一步:判断库存名额是否充足
- 第二步:减少库存名额,扣减成功就是抢到
那么我们一般是借助 redis 的 lua 脚本特性来进行库存的扣减,因为 lua 脚本就可以保证我们操作的原子性,从而防止我们的超卖。
# 2.3 用户限流防刷子
限流我们主要采用两种策略:
- 一种是同一用户 ID 限流
- 一种是同一 IP 进行限流
首先秒杀是一种非常快就结束的活动,我们无需担心限流是否会导致他的用户体验。正因为秒杀很快就结束了,如果一个人 1s 内只能请求 10 次,那可以限制一个 IP 最多只能请求 10 次,毕竟热点商品 10 次都还抢不到,那就是真的抢不到了。做限流会给我们的服务带来极大的好处,因为有一些流量可能是由机器产生的,而不是人工手点。限流策略如何实现可以参考相关文章。
# 2.4 页面资源静态化
URL 唯一化处理,我们在进行静态化的时候,要对页面资源进行提前的缓存,保证用户直接请求 URL 的时候,无需去解析请求头,无需重组 HTTP 协议,可直接找到静态资源进行返回,这个过程不要与后端做数据交互。一般静态资源我们要放在 CDN 上,借助于 CDN 的就近原则可以提高我们的响应速度和命中率,同时让 CDN 开启资源压缩,减少传输数据量,提高速度。
# 2.5 秒杀按钮置灰
一个秒杀活动可以能是 10 点开始,但是用户 9.59 就进来疯狂的点击秒杀按钮,如果这里让用户疯狂点击的话,那么无论对我们的前端还是对服务端都会产生交互压力。所以我们的秒杀一定是在未达到时间点的时候,不让用户去点。时间点一到,通过前端的定时器来将按钮的状态作为变更,让活动正常进行。
这种让秒杀按钮置灰的做法,也是我们减轻流量压力的一种方式。
# 2.6 秒杀真连接隐藏
如果说我们的秒杀在开始之前就被一些黄牛、刷子之类的知道了真正的请求格式,那么他们其实可以提前做好准备,通过机器等方式来进行提前请求,这样那又会造成压力真正达到了我们的服务器,也会造成正常人根本秒杀不到商品。所以我们要采取链接加密,每次秒杀无规律可言,只有时间到了,才能让用户看到真实秒杀地址。
# 2.7 异步处理订单
随着我们的用户参与秒杀已经成功,那么我们不应该同步地去返回用户秒杀结果,而是交由 MQ,将秒杀成功的消息放入队列并快速返回给用户,告诉它秒杀成功,放入队列的消息交由其他服务来消费并负责后续的处理。
当随着用户点击查看订单的过程,你的后端其实基本已经处理完毕。后端服务处理也要提升我们的 tps,不能太慢,防止用户查不到数据从而造成歧义。
这部分的核心就是:用异步去处理秒杀成功的后续的麻烦过程。
# 2.8 订单失败补偿
对于用户来说,我已经秒杀成功,那就要保证订单一定要处理成功,不能说因为服务处理的原因让订单丢了,导致无法履约,这会带来用户的投诉。通常我们都是用 MQ 的方式进行驱动,MQ 只要存在,就一定要消费成功。如果消费失败,就要抛出重试,同时配有报警机制及时进行预警处理。
# 2.9 服务降级
这种方式其实已经是下下下策了,用于我们的服务确实出了极大的问题,导致秒杀业务不能正常进行。比如,秒杀的价格写错了,变成了 1 块钱 1 个 iPhone。那么这时为了及时止损,我们肯定要做一个降级开关,不能任何交互,直接返回一些提示文案,例如,“秒杀已结束”。以上讲的是业务的错,那么还有可能是我们的服务器撑不住了,比如秒杀活动的峰值超出了我们的预料,那我们也要根据业务线来加降级。