秒杀功能接口设计与总结

最近在做秒杀的活动,期间发现了一些问题,特此做一些小结以供参考。具体的页面设计大致如下(本人大致勾勒了下,不是很正规):

秒杀流程可大致划分如下:

秒杀功能最主要的几点:

1、被秒杀的商品库存绝对不能卖超,这点最关键;

2、要特别注意防止秒杀活动被刷,不能出现商品都被同一个人用户秒杀的情况;

3、秒杀期间还要做好接口的限流功能,因为秒杀开始的那一刻,请求将骤增;

4、秒杀活动还未开始,防止恶意用户拿到秒杀地址来刷接口;

针对以上几点来展开分析

库存卖超?

首先分析下库存会卖超的原因:

1、并发时减商品库存的SQL未控制库存数量大于0;

2、同一个用户可能会多次秒杀到多个商品;

如上两点的解决方案是:

1、减库存时控制库存数量

UPDATE reward SET reward_count=reward_count-1WHERE reward_id=? AND reward_count >

2、同一用户多次秒杀到同一商品,这里通过通过 中奖记录表中的reward_id和username构成的联合唯一索引

UNIQUE INDEX rewardid_username(reward_id,user_name)

这样当一个用户秒杀到多个同一商品时入库会失败,然后显示捕获异常即可.

接口的限流?

当秒杀活动开始后的一小段时间内,大量请求过来,将对服务器造成较大的负载,造成一系列连锁的效应,如数据库连接异常、超时,缓存被击穿等问题。针对这些潜在的点就要设计好接口的限流功能

1、如果产品允许的话,秒杀的前端可以加上验证码校验来分散请求,防止机器人恶意刷单、暴力试探,但是我这里的项目中不允许使用(综合考虑产品设计以及活动场景、用户体验等因素),就像是12306中的验证码一样,可以很有效的拦截和分散相当一部分请求。验证码实现也很多,挑一款轻量级的就好了;

2、后端的话,通过redis作为缓存,使用自定义注解+拦截器的方式实现限流,30秒内同一用户名或IP至多访问5次,超过5次的话,用户和ip加入黑名单(具体的限制需和产品讨论确定),并且返回请求太频繁响应。自定义注解+拦截器实现限流 请移步这里 ,获取用户请求的IP 请移步这里

3、将后端秒杀的过程异步化,可一定程度上请求削峰,比如我做的活动中这个过程包括新增中奖记录、减库存、减抽奖机会、1年NBA会员充值等,只要你库存足够,用户秒杀就可以直接先返回秒杀成功,其余的这些处理过程可以留给异步化任务去完成。我这里就把1年NBA会员充值这个过程给放在ActivityMQ里面去了,其实这里要思考的点也很多,比如ActivityMQ里面的消息丢了怎么办? 有没有合适的重试机制?等等,这里就不再赘述了。实现异步化的方式也很多,比如java并发编程里的Executor框架(包含线程池、异步任务等)也是极好的,关于Executor框架(包含线程池、异步任务等),请移步这里

4、为了减少与mysql数据库的交互,我把每个用户对应的抽奖机会放在了redis缓存中(考虑到这个秒杀活动上线时间不长),这个抽奖机会是用户购买了礼包后添加上去的,在用户支付成功后回调逻辑中给用户添加的秒杀机会,省的秒杀接口逻辑中去数据库中load用户的秒杀机会数。

5、使用缓存后,要思考的点就更多了。经典的缓存击穿、雪崩、缓存和数据库双写一致性、并发竞争问题。缓存击穿和雪崩我们没考虑这种糟糕的场景,因为我们产品的日活跃量在3w+左右(我指的是活动落地页的日活跃量,不是普通的页面PV/UV之类的)。关于缓存和数据库双写一致性的问题,只能说,如果一定要100%保证数据的强一致性,那就不要使用缓存了,我们只能通过适当的缓存更新策略(比如更新数据库后及时删缓存、缓存更新可能失败,是否要建立补偿机制等)来降低数据不一致的概率。针对缓存key竞争读取问题,可以加分布式锁实现。关于这些点,我这里也没有纠结在缓存上,只要量没到那个级别,做的再多工作都是画蛇添足,如果上线后出现了问题,再针对具体问题对症下药,这里只是提一些要考虑的点。

活动未开始秒杀接口就被刷了?

由于现在的浏览器都支持查看源码,页面和js就暴露出来,自然包括秒杀接口的URL。针对这个问题,我们接口里明确限制了活动的开始和结束时间,比如活动时间是2018-05-05 09:00:00~2018-05-05 12:00:00,那么秒杀接口逻辑里除了限流和严格控制库存之外,就要判断请求进来的时间点是否落在活动的开始-结束时间之内,不是的话就直接返回错误响应了。

注意事项:

1、秒杀接口里面的逻辑中最好不要加锁去控制,加锁将严重影响秒杀接口的吞吐量,加锁后,锁的时间具有很大的不确定性,你不知道锁多久才会释放;

2、写完接口后一定要模拟并发环境测试接口,我是用jmeter压测的。压测时要考虑活动页的活跃用户量,比如我的3w+,那你至少要用3w+的线程数去压测,而且最好用

10台电脑,每台电脑的jmeter开3000个线程同时去压测,保证测试结果的正确性和真实性。可以写好jmeter的压测脚本后copy到其他电脑,但是整合10台电脑资源还是稍稍麻烦的。

闲言:要真的有高手刷你的接口就比较尴尬了,毕竟你所有的防护措施只防君子不防小人。暂时就想到这么多了,有问题的话后续再补充更新。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180614G147PY00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券