这里写目录标题
一:秒杀应该考虑哪些问题
1.1:超卖问题
分析秒杀的业务场景,最重要的有一点就是超卖问题,假如备货只有100个,但是最终超卖了200,一般来讲秒杀系统的价格都比较低,如果超卖将严重影响公司的财产利益,因此首当其冲的就是解决商品的超卖问题。
解决方式
项目肯定是集群部署,扣减库存的逻辑在一段代码中,那么给这段代码加锁。
扣减库存逻辑{
// 校验库存。查询库存数量是否大于0
// 扣减库存。
// 创建订单
}
1、分布式锁。因为集群部署,所以加的是分布式锁。
2、乐观锁:
------------service层--------------
@Override
public int createOptimisticOrder(int sid) throws Exception {
//校验库存。 通过商品id查找到当前商品的库存信息(sid、sale、version...)
Stock stock = checkStock(sid);
//乐观锁更新库存
saleStockOptimistic(stock);
//创建订单
int id = createOrder(stock);
return stock.getCount() - (stock.getSale()+1);
}
private void saleStockOptimistic(Stock stock) {
LOGGER.info("查询数据库,尝试更新库存");
int count = stockService.updateStockByOptimistic(stock);
if (count == 0){
throw new RuntimeException("并发更新库存失败,version不匹配") ;
}
}
---------Mapper------------------------
<update id="updateByOptimistic" parameterType="cn.monitor4all.miaoshadao.dao.Stock">
update stock
<set>
sale = sale + 1,
version = version + 1,
</set>
WHERE id = #{id,jdbcType=INTEGER}
AND version = #{version,jdbcType=INTEGER}
</update>
1.2:高并发
秒杀具有时间短、并发量大的特点,秒杀持续时间只有几分钟,而一般公司都为了制造轰动效应,会以极低的价格来吸引用户,因此参与抢购的用户会非常的多。
短时间内会有大量请求涌进来,后端如何防止并发过高造成缓存击穿或者失效,击垮数据库都是需要考虑的问题。
解决方式
1、保证redis高可用。redis集群部署,采用redis-cluster高可用方式;
2、限流。100w的请求量,通过nginx(令牌桶)、histys进行限流,只放20w请求进来;
3、秒杀页面静态化。将商品的描述、参数、成交记录、图像、评价等全部写入到一个静态页面,用户请求不需要通过访问后端服务器,不需要经过数据库,直接在前台客户端生成,这样可以最大可能的减少服务器的压力。具体的方法可以使用freemarker模板技术,建立网页模板,填充数据,然后渲染网页。
1.3:接口防刷
现在的秒杀大多都会出来针对秒杀对应的软件,这类软件会模拟不断向后台服务器发起请求,一秒几百次都是很常见的,如何防止这类软件的重复无效请求,防止不断发起的请求也是需要我们针对性考虑的
解决方式
1、同一个用户xx秒内重复请求直接拒绝。具体的做法就是通过redis的键过期策略,首先对每个请求都从String value = redis.get(userId);如果获取到这个value为空或者为null,表示它是有效的请求,然后放行这个请求。如果不为空表示它是重复性请求,直接丢掉这个请求。如果有效,采用redis.setexpire(userId,value,10).value可以是任意值,一般放业务属性比较好,这个是设置以userId为key,10秒的过期时间(10秒后,key对应的值自动为null)
1.4:秒杀url
对于普通用户来讲,看到的只是一个比较简单的秒杀页面,在未达到规定时间,秒杀按钮是灰色的,一旦到达规定时间,灰色按钮变成可点击状态。这部分是针对小白用户的
如果是稍微有点电脑功底的用户,会通过F12看浏览器的network看到秒杀的url,通过特定软件去请求也可以实现秒杀。
或者提前知道秒杀url的人,一请求就直接实现秒杀了。这个问题我们需要考虑解决。
解决方式
1、将秒杀的url实现动态化。即使是开发整个系统的人都无法在秒杀开始前知道秒杀的url。具体的做法就是通过md5加密一串随机字符作为秒杀的url,然后前端访问后台获取具体的url,后台校验通过之后才可以继续秒杀
1.5:数据库设计
秒杀有把我们服务器击垮的风险,如果让它与我们的其他业务使用在同一个数据库中,耦合在一起,就很有可能牵连和影响其他的业务。
如何防止这类问题发生,就算秒杀发生了宕机、服务器卡死问题,也应该让他尽量不影响线上正常进行的业务。
解决方式
1、应该单独设计一个秒杀数据库,和其他表隔离开,防止因为秒杀活动的高并发访问拖垮整个网站
1.6:大量请求问题
按照1.2的考虑,就算使用缓存还是不足以应对短时间的高并发的流量的冲击。如何承载这样巨大的访问量,同时提供稳定低时延的服务保证,是需要面对的一大挑战。
我们来算一笔账,假如使用的是redis缓存,单台redis服务器可承受的QPS大概是4W左右,如果一个秒杀吸引的用户量足够多的话,单QPS可能达到几十万,单体redis还是不足以支撑如此巨大的请求量。缓存会被击穿,直接渗透到DB,从而击垮mysql。后台会将会大量报错。
参考链接:
系统如何达到秒杀而不被薅羊毛
秒杀系统如何防止超卖?