我这边是在京东某部门负责营销活动的开发,大家所知道的秒杀其实也是活动的一种,这些活动有个共同点,就是流量极大.首先每个商品你都要知道他是不是活动商品?属于什么活动商品?我有没有购买资格?活动的开团时间是什么?活动有哪些商品,每个商品的活动叠加结果是什么?还有许多活动有库存的概念,比如秒杀或者限量购,逻辑很复杂,这里里面有太多太多的细节,很多细节可能没法一一说清楚,这里总结些重要的秒杀的问题以及解决方案给大家;
这里我以我做的一个最有意思的营销活动,给大家介绍一下,不涉及技术,后面出案例会以此为案例而已,看技术可以跳过,我这个活动叫做《周期循环购》,这个活动可以设置好参与的商品,然后支持设置活动总的有效时间之外呢,我们还支持让你选择周一到周日哪几天是开团天,每个开团日再进行设置哪几个时间段为开团时段,然后还支持设置商品在每个时段的库存是多少,那么其实在这里看,团购对于我来说就是一个产品的概念(SPU),而我的营销活动限制的才是真正的库存(SKU),另外这个又涉及到周期性库存的概念,每天每个事件段都有一个自己的独立库存,这比单一的商品库存要复杂许多许多。
正题:
一.明确团购秒杀为什么不好做?
不好做,说道理是由于团购属于折扣商品,通常价格比较低,流量真的极大,并发访问下容易造成各种并发症,举几个例子如下:
- 数据库负载极高,万一挂了就玩球了,毕竟数据库我们都知道抗压能力不咋地;
- 流量不均匀,有些时候会有部分热门商品(比如首发的iphone)特别高频访问,把服务器资源全给占用了其他商品就被搁置排队了;
- 逻辑复杂,下单和回滚或者取消订单要保证幂等,防止数据不一致以及超买超卖的可能,万一造成资损,大家都知道这个的严重性;
二.如何解决秒杀可能带来的问题呢?我这里提出一些我在项目里使用的经验和技巧
2.1.解决高流量问题
首先流量过高
是根本原因,因此我们最先解决这部分问题;
2.1.1 访问类请求;
-
隔离
动态数据和静态数据,静态数据可以前置到用户端或者CDN里,比如商品图片这玩意,这样非必要的请求就可以拦截一部分了; - 后端数据加
缓存
,让高性能的玩意直面高并发,比如我上面提到的营销活动,目前基本上做到了查询全部缓存化,所有查询全部走Redis缓存,大家都知道Redis的性能吧; -
限频
,比如限制用户对于同一商品详情页的刷新次数,大家是不是经常会在活动开始的时候疯狂刷新....到刷新频次的时候可以显示的提示,也可以默默的请求无效化,另外这种限制尽量前置化
, -
层层过滤
,另外涉及到数据校验的,如果大家的系统有多个层次(比如前端->api站点等->server),最好做好层层过滤,每个层次都将一部分数据拦截了,比如前端可以做限频,api站点层做风控,数据校验等等,这样最后有效请求就少了许多了;
2.2.2 下单类请求(很多和上面一样的就不说了,比如层层过滤)
削峰
,如果我们的流量极大,我们可以进行削峰限流,推荐大家采用异步下单的方式,安全可靠,所谓削峰限流我的理解是可以看做银行柜体,就算流量再大,你都要排队,能处理多少看银行的能力,我有一个银行就处理慢点,有十个银行就处理快点,没啥问题,大不了你就办不成功呗,当然这里注意防止丢单,可以采用分布式事务;延缓请求
我们可以订阅系统负载,或者一些监控服务,在系统流量极大的时候,触发答题系统,在大家进行下单的时候进行答题,那么有的人答题快有的人答题慢,大家拼的就不全是手速了,还有脑速,哈哈,最主要的是把开团那一瞬间的流量均匀到答题的时间了,这点效果显著;-
精准限流
,比如我们可以采用付定金预购的方式先把购买人群锁死,有几个好处:- 第一看的人少了,你没付定金到时候你看他干啥?
- 第二 买的时候尝试下单的人少了,只有付了定金的允许下单
- 第三呢由于定金的存在大家买的时候会慎重一些减轻了退货啥的造成无用订单以及商家卖不出去情况;
-
库存缓存化
,我们可以把库存做到redis里,我们先采用lua(预查redis库存->再预扣redis的方式)->乐观锁扣除mysql库存- 为什么这里不直接decr预扣redis要用lua先查redis再预扣?主要是考虑到由于秒杀团购等属于赔钱引流的生意,所以大部分请求都是远远大于库存的,因此极有可能买不到,如果我们直接扣redis,那扣失败了,咱们不还是要还库存加回去吗
- 另外呢,要注意由于库存的特殊性,我们要保证redis里的数据和mysql 的数据的一致性,因此我们要使用一些事务来保证,比如TCC或者MQ事务实现;
- 这里咱们将mysql库存扣减放到了最后一步也减少了mysql锁竞争的过程了,这性能飞升啊;
2.2.3 一些别的优化
- 引入
热点发现,数据隔离,精准路由
机制,正如我前面所说某些商品确实牛逼,流量极大,虽然这部分数据极少,但是却有可能会挤压正常服务;- 热点发现又分为
静态发现和动态发现
,所谓静态发现
呢就是在商品还没卖,我们就知道这个是不是热点,比如京东这边呢就有根据店铺流量,过往同类商品售卖情况啥啥的智能打标签服务,有个预判,一下就知道你是不是热点了,动态发现
是某些商品咱们不知道他这么牛逼,然后卖的时候一下就火爆了(商家都惊喜的笑醒了); -
数据隔离、精准路由
,数据隔离,这部分极其热点的数据呢可以把他预热到指定的热点缓存和热点数据库避免影响普通商品,另外为了避免影响呢,我们还要将这部分商品路由到指定服务器进行操作,路由到指定服务器还不够,甚至我们还要加队列进行排队,引入极热点数据的异步下单过程,另外优化的点就是如果前面已经卖完了就不要傻傻的再一个个排队尝试了,直接提前结束,提前通知;
- 热点发现又分为
多级缓存
机制,虽然说Redis单机就十来万的并发就比较牛逼了,但终归还是个第三方服务的网络查询,有的时候redis使用率过高也可能造成瓶颈,如果我们可以将部分不会变的数据进行本地化,进行本地缓存,那这样是效率最高的,用啥都没这个好使,另外也分担了redis的压力。啥是不会变的呢?比如活动信息,很多参与618,1111的秒杀大促活动是不允许随便变更数据的;降级次要请求
,在下单等重要服务压力极大的情况下,我们可以降级次要请求,将服务器性能给重要请求用,比如说降级评论查询,只允许查几页,只允许查一层。另外像双十一这样的超级活动日,可能会降级很多服务,比如不允许活动日退货啊啥的;特殊手段做特殊操作
,比如团购下单接口有个订单15分钟超时取消订单的操作,但是呢我们有时候没有办法一下子处理那么多订单,让他过期,比如有十万个订单同一时间过期,如何做到呢?这种超时订单是绝对不能继续让他进行支付的,那么我们可以进行数据的另类同步,我们可以在任何查出到这个订单的地方都对状态进行两种判断,比如1 订单状态为超时,2 订单状态为下单中,订单下单时间距离现在超过了15min,这两种状态咱们都认定他为超时,这样呢我们就可以做到一定程度上的订单同一时间超时了
今天关于应对高流量呢就到这了,后面如果看的人多,我会再说些下单和库存问题的优化啥的;