秒杀难点:
1、突发流量、数据热点
2、数据一致性、短暂混沌态
如果采用传统的数据库进行数据存储,对同一资源的争抢,就会面临严重的锁冲突问题。一般是通过一个前置的,速度更快的存储顶在前面,这就涉及到源库和目标库的数据同步问题。
从商品资源的上架,到秒杀的完成,会经历一个短暂的混沌状态,出现数据不一致的情况。在请求量非常集中的情况下,还会产生并发问题,个体的行为和结果,是不可预测的。
系统层面,秒杀业务的优化方向如何?
(1)将请求尽量拦截在系统上游,而不要让锁冲突落到数据库。
(2)充分利用缓存。
秒杀业务,可以使用典型的服务化分层架构:
端(浏览器/APP),最上层,面向用户
站点层,访问后端数据,拼装html/json返回
服务层,屏蔽底层数据细节,提供数据访问
数据层,DB存储库存,当然也有缓存
数据预热
有的系统进行秒杀的,可能就那么几件商品,手工录入都玩得转。对于平台类型,或者用户量巨大的app,秒杀的商品就不再那么简单。
中间,会进行一些数据的合并,或者二维展开,也就是准备秒杀的数据。这些数据进行处理之后,会提前进入到秒杀系统进行数据预热。
秒杀阶段进行的是否顺利,得看数据准备的是否合理。
请求承载
这是请求的最外层,属于接入层。做的好的系统,能够在接入层就屏蔽大部分请求,极大的减轻后端服务的压力。
连接数承载
对于接入层来说,首先一个挑战就是连接数。一般互联网的接入层,就是lvs+nginx。这里会涉及到对操作系统的优化,以及对于nginx本身的优化。
并发承载
对于落到某台机器上的请求,依然会存在并发高的问题。线程池使用要合理,怎么去进行过滤,合并等,有一定挑战。
负载均衡
请求要能真正的做到均衡,不能产生热点问题。比如nginx的ip_hash,虽然能一定程度上规避分布式session问题,但请求会不均衡。
重试?
秒杀业务不要配置重试,会加剧系统负载。请求失败?那就再来一次。
系统隔离
秒杀业务占用的系统资源,和正常运行的系统严重不对等。如果有条件,秒杀系统的硬件和服务环境,要与正常业务系统进行一定程度的隔离。要提前评估对其他服务的压力,避免影响正常业务。
CDN
对于html,js,css,图片等内容,占用了大量的带宽。如果将这些资源都放在自己的服务器上,流量到来时会迅速占满带宽,造成正常的秒杀请求无法完成。CDN可以有效解决这个问题。剩下的请求,就是真正的秒杀业务。
减小请求包
对网络请求包,要进行大量优化。可以开gzip压缩,资源本身也进行压缩,去掉请求包中的无用信息,对网络报文进行精简。
请求拦截
秒杀系统一个非常大的原则,就是要把尽量多的无效请求,拦截在外部。请求拦截,可以分为上游拦截和业务拦截。
上游拦截
拦截方面,存在一个全局的设置。当系统判断以及达到瓶颈阶段,就可以通过全局限流方式进行服务降级,对于一些次级服务,要进行熔断处理。对于前端来说,也是需要进行一些优化的。比如浏览器缓存,防重入验证等,能够拦截数量可观的请求。
业务拦截
除了一些全局的限制,对于大部分请求来说,和用户是息息相关的。一个用户,可能会频繁的刷新,或者绕过前端,直接使用软件调用后端接口。这些用户的非法请求,也要进行拦截。
同时,用户对资源的争夺,也不应该无限等待。比如100个库存的商品,第1w个请求到来的时候,就不需要再排队等待了,直接返回秒杀完毕就ok了。
排队方面,会用到jvm内排队,也会用到外部的消息队列,mq,进行请求的缓冲。
数据缓存
缓存,可能是秒杀系统中最重要的一个组件了。从前端缓存,到jvm缓存,再到分布式缓存,都会对系统性能产生数量级的提升。值得注意的是,由于秒杀系统严重依赖缓存系统,所以缓存系统需要做高可用。
缓存的读操作,要考虑数据的加载和同步。写请求,就要考虑数据的合并与并发写入,数据的一致性等。虽然缓存的速度比起DB来,快了很多很多,但它的性能总是有瓶颈的,相关代码要着重优化。
安全
技术的门槛越来越低,二年级的小学生都开课教swift了,写个秒杀插件什么的,不费吹灰之力。秒杀系统的安全性比较重要,应该说和钱打交道的系统,都是容易出问题的。你要是想薅羊毛,认准营销、秒杀业务,准没错。
要尽量提高作弊门槛,比如url动态化,从入口就隔绝了大部分攻击;验证码,只会增加攻击者的成本。有些安全性级别较高的,还会增加风控规则,比如同一ip请求过多封禁、账号注册日期三天之内不允许参与、秒杀的门槛必须是金牌会员等。
我曾经经历过一次,对方本来是一个算加减乘除的验证码,脚本都写好了。结果秒杀前5分钟,验证码12306附体,xjjdog直接放弃了。
躲在幕后的DB
在整个秒杀系统中,传统的DB,只能灰溜溜的躲在幕后(小流量除外)。我要是DB,也会躲在后面瑟瑟发抖。
DB的数据,要提前载入到秒杀系统中进行运算,秒杀完毕,还要把狼藉的数据进行落地与清算。在笔者见过的不少秒杀场景中,甚至不需要DB的参与,真是艺高人胆大。
这就引申出另外一个问题。假如缓存系统出现问题,请求要不要穿透呢?我的建议是,不需要。非正常的请求,会瞬间压垮DB,产生更加严重的数据错乱问题,假如没有做隔离,后果会更狂野。与其错了,不如认怂,乖乖的复盘写故障报告吧。