高并发秒杀系统优化

首先我们要清楚,高并发发生在哪些部分,然后正对不同的模块来进行优化
红色为高并发对性能影响较大的部分。
高并发秒杀系统优化_第1张图片


1.详情页部署到CDN节点上

用户在秒杀开始之前,会对详情页做大量的刷新操作。
所以我们将详情页部署到CDN上,CDN将详情页做静态化处理。
这样其实用户访问的静态页的html已经不在秒杀系统上了,而在CDN节点上。
高并发秒杀系统优化_第2张图片
其实访问静态资源和静态页面是不用访问我们的系统服务器的,所以我们要单独做一个请求来获取服务器的时间,这样保证CDN和秒杀系统服务器的秒杀开始时间同步。

PS:什么是CDN?
高并发秒杀系统优化_第3张图片

(1)CDN本质用于加速用户获取数据,这个数据可以是静态资源,也可以是动态资源,取决于我们的推用策略。甚至大部分视频加速依赖于CDN。
(2)一般运营商的CDN部署都是在离用户最近的网络节点上。
(3)命中CDN之后不需要访问后端服务器。

所以秒杀系统访问详情页的HTML CSS JS信息是不用访问服务器的。


2.秒杀地址接口使用服务器端缓存,Redis

随着时间推移,开始秒杀,到秒杀关闭,这都是秒杀地址给我们的数据。这部分适合使用服务器端缓存,Redis或者早期的Memcache。
这样他就可以抗很高的QPS,官方数据10W/s的QPS。 Redis集群化之后可以抗更高的QPS.
高并发秒杀系统优化_第4张图片
高并发秒杀系统优化_第5张图片


3.秒杀操作优化

难点分析:
1.大部分写的操作和核心操作无法使用CDN
2.不可能在缓存中减库存,你在redis中减库存,那么其他用户也可能通过缓存来减库存,这样库存会不一致,所以要通过mysql的事务来保证一致性
3.比如一个商品所有人都在抢,那么会在同一时间对数据表中的一行数据进行大量的update set操作。
高并发秒杀系统优化_第6张图片
解决方案:
目前常用的一个方案架构:
高并发秒杀系统优化_第7张图片
(1)用redis或者NoSQL实现一个原子的计数器,这个原子计数器就用来记录这个商品的库存,当你秒杀到一个商品时,原子计数器就减一。可以保证原子性。
(2)减库存成功后,就会记录其行为,也就是谁减了库存。记录下来之后,作为一个消息放到一个分布式的MQ(消息队列)之中。比如RabbitMQ ,RocketMQ,ActiveMQ,Kafka.
(3)消息放到MQ中之后,后端的服务会去消费这个消息并落地,存放到MySQL中。

这就是服务层拦截(反正就是不要让请求落到数据库上去)

服务层怎么拦截?大哥,我是服务层,我清楚的知道小米只有1万部手机,我清楚的知道一列火车只有2000张车票,我透10w个请求去数据库有什么意义呢?没错,请求队列!
对于写请求,做请求队列,每次只透有限的写请求去数据层(下订单,支付这样的写业务) 1w部手机,只透1w个下单请求去db
3k张火车票,只透3k个下单请求去db 如果均成功再放下一批,如果库存不够则队列里的写请求全部返回“已售完”。

缺点:
(1)NoSQL没有MySQL稳定,分布式组件需要运维保持其稳定性。
(2)幂等性难以保证,一般还要维护一个分布式NoSQL来记录哪些用户已经秒杀过了


MySQL真的低效吗?

当我们执行update操作之后,然后insert购买明细信息,之后才commit,这才是一个事物的完整流程,如果在commit之前其他用户想要操作这行数据,只能等待行锁的释放。
这样就造成了阻塞。
高并发秒杀系统优化_第8张图片

Java和数据库交互也需要一定的时间。
mysql本身可抗4w的QPS,但因为网络延迟,或者GC的原因,造成没秒500次左右。
高并发秒杀系统优化_第9张图片


总结

1.尽量将请求拦截在系统上游(越上游越好);(不要让锁冲突落到数据库上去)。传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,并发高响应慢,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小。以12306为例,一趟火车其实只有2000张票,200w个人来买,基本没有人能买成功,请求有效率为0。
2.读多写少的常用多使用缓存(缓存抗读压力);秒杀买票,这是一个典型的读多些少的应用场景,大部分请求是车次查询,票查询,下单和支付才是写请求。一趟火车其实只有2000张票,200w个人来买,最多2000个人下单成功,其他人都是查询库存,写比例只有0.1%,读比例占99.9%,非常适合使用缓存来优化。

高并发秒杀系统优化_第10张图片

按钮防重复
回顾我们下单抢票的场景,点击了“查询”按钮之后,系统那个卡呀,进度条涨的慢呀,作为用户,我会不自觉的再去点击“查询”,对么?继续点,继续点,点点点。。。有用么?平白无故的增加了系统负载,一个用户点5次,80%的请求是这么多出来的,怎么整?

产品层面,用户点击“查询”或者“购票”后,按钮置灰,禁止用户重复提交请求;
JS层面,限制用户在x秒之内只能提交一次请求;

你可能感兴趣的:(Java,Concurrency)