应用情景:12306抢票,春运期间,一票难求!票的数量是有限的,但也罩不住中国出门打工的人多啊,如果放任所有请求去读库,哪个数据库能扛得住?
优化草案
将请求尽量拦截在上游
如果将所有的请求都落到数据层,数据读写时锁冲突严重,并发响应就会很慢,用户体验就会很差。所以可以让系统中的每一层都做些事情,不要只做一个过渡者
,尽量拦截下一些无效的请求,给数据库减压。
利用缓存
12306抢票是一个典型的读多写少
的应用场景,比如一个班次的票有2000张,有200w人抢票,刷票时就是读,抢到票就要写,读写比是1000:1, 是非常适合用缓存的,其实大多数企业应用都是读多写少,都可以用缓存。
系统架构
下面是一个常见的站点架构图,高并发的优化就从这张图说起。
- 浏览器: 与用户交互,会执行js
- 站点层: 会访问服务层,返给浏览器界面
- 服务层: 复杂的业务
- 数据层: 封装访问数据的复杂,访问数据库
各层优化
客户端优化(浏览器、App)
- 可以做防重复提交,按钮点击后置灰
- 可以用js限制x秒内只能提交一次请求
这样可以挡住普通用户,但是挡不住一些黄牛的for循环,好的是99%的都是普通用户,最起码能拦截下80%的无效请求。
站点层优化
客户端防重复提交也只能拦住普通用户,在黄牛盛行的今天,我们是很有必要连黄牛的请求也一并拦截!那么怎么样来拦截黄牛的for循环呢?其实也是类似客户端的防重复提交,对于每次请求,我们都能拿到用户ID,结合业务我们可以对请求进行计数,判断x秒内只能透过一次请求,这个计数可以放在session中,这样就能裆下黄牛99%的for循环请求。
但是有些黄牛手里有N(=10w)个肉鸡,N个账户,同时请求。站点层的拦截就挡不住了!
服务层优化
不管怎么样,搞死不让请求落到数据库,那么我怎么拦截肉鸡的高并发请求呢?利用队列!
比如,有2000张票,有20w用户想要下单购买,请求已经到了服务层,这时候可以把请求加入队列,如果队列长度大于2000(或者大于余票)就把其他请求拦截下来。
数据层
客户端拦截了80%,站点层拦截了99%,服务层又有队列控制,数据库基本无压。
业务优化
一切脱离业务的架构都是耍流氓
!比如,抢票时间可以调整一下,分时分次抢票
,8点卖一批次,9点卖一批次,…。将流量均摊。其次,粒度的优化,我们有时候只关心有票和无票,二不关心具体还剩多少票,所以只需要做一个粗粒度的缓存即可。
预备
如果并发量实在太大,可以对站点层的机器进行扩充,通过增加机器来分担压力。
总结
高并发架构优化的基本思路:
- 尽量将请求拦截在上游
- 读多写少多用缓存
- 结合业务来优化