秒杀系统的优化


秒杀系统简介

秒杀场景一般会在电商网站举行一些活动或者节假日在12306网站上抢票时遇到。对于电商网站中一些稀缺或者特价商品,电商网站一般会在约定时间点对其进行限量销售,因为这些商品的特殊性,会吸引大量用户前来抢购,并且会在约定的时间点同时在秒杀页面进行抢购。

秒杀系统主要面临三大问题:

一、瞬时的高并发访问。抢购和普通的电商销售有所不同,普通的电商销售,流量是比较平均的,虽然有波峰波谷,但不会特别突出。而抢购是在特定时间点进行的推销活动,抢购开始前,用户不断刷新页面,以获得购买按钮;抢购开始的一瞬间,集中并发购买。

二、数据正确性。抢购毕竟是一种购买行为,需要购买、扣减库存、支付等复杂的流程,在此过程中,要保证数据的正确性,防止超卖(卖出量超过库存)的发生。

三、防作弊。

秒杀系统的设计理念

秒杀系统的优化或者设计理念为以下几点:

限流: 鉴于只有少部分用户能够秒杀成功,所以要限制大部分流量,只允许少部分流量进入服务后端。

削峰:对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。实现削峰的常用的方法有利用缓存和消息中间件等技术。

异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。

内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。

可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了。像淘宝、京东等双十一活动时会增加大量机器应对交易高峰。

前端方案

页面静态化: 将页面上的所有静态元素全部静态化,将其交给ngix管理,以此同时采用cdn来N来抗峰值;对于动态部分采用Ajex请求动态加载数据

静态资源优化:主要是讲多个css/js请求合并为一个等。

秒杀接口隐藏:在秒杀开始前一段时间才暴露出秒杀的接口(路径),同时在后端可以通过与用户id绑定生成每个用户的秒杀路径,保存在redis中,在固定时间中只允许固定的次数请求,过多的请求被拦截。这样防止使用脚本等进行大批量的请求。

验证码:主要是为了流量削峰与筛除简单脚本。通过用户输入验证码的时间,将在某一时间的突发流量均摊到随后的一段时间,还能筛除掉一部分简单的无法识别验证码的脚本。

后端方案

用户限流:通过对用户id绑定生成的秒杀路径,限定访问次数

redis缓存: 将数据库内容比如秒杀商品内容详情缓存到redis,不访问数据库

消息队列: 通过将请求缓存到消息队列,异步执行来削减同一时间到来的请求。

CDN:内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。简单的来说,就是把原服务器上数据复制到其他服务器上,用户访问时,那台服务器近访问到的就是那台服务器上的数据。CDN的劣势是内容的变更生效慢,所以仅适用于“几乎不变”的资源,例如引用的js包,图片等。

秒杀系统的具体实现

秒杀开始前:

只能看到看到秒杀商品详情,无法进入秒杀接口,直到秒杀开始前一段时间。

对进入秒杀商品详情页的用户将其uid与秒杀商品gid结合,生成该用户对该秒杀商品的秒杀接口路径,将其保存到redis中。

在秒杀商品详情页页面采用js脚本来实现倒计时与限制秒杀按钮的电机。

秒杀开始时:

服务端使用redis提前缓存秒杀商品详情,主要参数为库存数量。

使用redis来实现秒杀商品的预减,不直接访问数据库。当redis中的商品库存少于0,拒绝请求。这样能保证只有少量的请求能够接近数据库。

将抢到预减的请求放入到消息队列中,将同步转为异步执行,实现流量的再次削峰。

服务层从消息队列中拿出请求,进行数据库操作,实现真正的商品抢购事务,生成订单;若事务失败,回滚事务,告知用户抢购失败。若事务成功,请求用户确认订单详情,并支付。

注意事项:

防止买超: 需要对数据库字段进行设计,添加索引;在服务端需要对库存字段进行限制,保证不为负。

消息队列: 也可以使用redis来充当消息队列。

用户限流: 限制用户一段时间内能点击的次数

IP限流: 方法太过于粗暴,容易误封无辜用户,不推荐。

-------------本文结束感谢您的阅读-------------