Java高并发系统设计及其优化策略——秒杀系统(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Jorocco/article/details/82184577

1、项目介绍
该项目是基于SSM框架的一个秒杀系统,采用了CDN技术用于加载静态资源,redis缓存技术承载高并发获取商品信息,针对mysql数据库事务性访问采用了存储过程技术以支撑每秒数万的访问量。
2、技术要点
1、建表的时候采用联合主键,将seckil_id和user_phone作为联合主键可避免重复秒杀。
2、MD5加密秒杀接口,可以防止用户推测出秒杀地址
3、增加一个dto层用于解析Controller层返回的Json格式数据
4、秒杀地址接口采用redis进行优化,并采用protostuff序列化反序列化对象提高传输效率
5、mysql事务交互的优化。

3、高并发系统常采用的优化策略

如何设计一个高并发的系统?例如同一时刻有几万并发请求。

3.1 高并发系统
1、使用Reactor模式,即将线程进行细化,交给全局管理者selector进行全局调度,以提高线程的并发。
2、采用消息队列进行并发、分流
3、优化数据库,合理的事务隔离级别、SQL语句优化、索引的优化
4、使用缓存,如redis,尽量减少数据库IO
5、使用分布式数据库,服务器负载均衡,如niginx反向代理
6、过滤非法IP数据包

策略
一、分布式计算
1、对系统功能进行分布式部署,核心生产流程进行异构化。
一个电商系统,接受用户订单的功能、并完成支付是优先级最高的,其次是对订单的生产,最后是运营人员对用户订单的管理。那么就可以根据优先级把系统设计成:Web_Tomcat 、Order_Produce、Manager_Tomcat三个子系统。如果再进一步,还可以把支付功能从Web_Tomcat中拆出来,做成Pay_Tomcat四个应用。四个应用共同完成一笔订单交易。将系统拆分成多个应用,系统计算订单能力提升,扩展性得到提高。

二、多机房入口
增加请求入口,把用户请求分散到多个机房

三、CDN加速
选择最优路径将每个请求快速的传递到机房

四、HTML页面资源静态化
采用ngnix服务集群,将静态资源部署在ngnix服务器上,ngnix不需要访问webapp即可响应用户,减少应用渲染页面的时间

五、缓存
在数据库层上加一层缓存,减少对数据库的访问压力,缓存的数据都是存储内存中,速度可以大幅度提升

六、数据库拆分
当数据量达到某个阀值时,数据库拆分就会成为一个紧急的需求。一般从业务上进行垂直拆分,如果业务单一,也可从水平上进行拆分。拆分的原则一般是:避免跨数据库事务和如何选择shardingId(分片)。跨数据库事务可以选择在前期调研时把同一事务中的表放在一个数据库中。如果数据冷热不均shardingId可以是UserPin或者订单号Hash打散后的值,如果数据冷热均匀可以按段分库也可以对某一个值取模后的值。
多数据库事务管理,现在业界有很多已成型的中间价,如阿里的Corba 360的 MyBatis本身的代理等。大致可以分为两类,一类是在Webapp层进行选择数据源,一类是在代理层面上对SQL语句解析选择数据源。后者需要配置shardingId,只有通过shardingId作为where的SQL语句才能针对某个数据源进行操作,其余都是对所有数据源操作。
Spring本身也提供了一部分选择数据源的功能,如AbstractRoutingDataSource和一些懒加载的数据源代理类等,也可以自己包装JdbcDataSourceTransaction实现对多数据源的事务管理。在事务开启时,传入shardingId路由数据源然后对其进行SQL操作。这种方式会比第三方中间件更灵活,但对开发者的要求也更高。

3.2 网页响应慢
现在一个网页响应速度明显变慢了,假如我把这个任务交给你,你怎么处理这个问题 ?

1、升级带宽,选择带宽充足的服务器
2、网站内部优化,网页代码优化,多使用css样式,图片优化,尽量控制在kb以内,优化程序,多使用静态网页

4、架构设计优化方案

4.1、秒杀系统架构设计优化
一个常规的秒杀系统从前到后,依次有:
前端浏览器秒杀页面=》中间代理服务=》后端服务层=》数据库层

根据这个流程,一般优化设计思路:将请求拦截在系统上游,降低下游压力。在一个并发量大,实际需求小的系统中,应当尽量在前端拦截无效流量,降低下游服务器和数据库的压力,不然很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时。

整体设计思路和优化点:
限流:屏蔽掉无用的流量,允许少部分流量流向后端。

削峰:瞬时大流量峰值容易压垮系统,解决这个问题是重中之重。常用的消峰方法有异步处理、缓存和消息中间件等技术。

异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。

内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。

可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了。像淘宝、京东等双十一活动时会增加大量机器应对交易高峰。

消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。

充分利用缓存:利用缓存可极大提高系统读写速度。

4.2 详细方案
这里写图片描述
4.2.1 前端方案

静态资源缓存:将活动页面上的所有可以静态的元素全部静态化,尽量减少动态元素;通过CDN缓存静态资源,来抗峰值。

禁止重复提交:用户提交之后按钮置灰,禁止重复提交

用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流

4.2.2 中间代理层
可利用负载均衡(例如反响代理Nginx等)使用多个服务器并发处理请求,减小服务器压力。

4.2.3 后端方案

控制层
限制同一UserID访问频率:尽量拦截浏览器请求,但针对某些恶意攻击或其它插件,在服务端控制层需要针对同一个访问uid,限制访问频率。

当用户量非常大的时候,拦截流量后的请求访问量还是非常大,此时仍需进一步优化。

1.业务分离:将秒杀业务系统和其他业务分离,单独放在高配服务器上,可以集中资源对访问请求抗压。

2.采用消息队列缓存请求:将大流量请求写到消息队列缓存,利用服务器根据自己的处理能力主动到消息缓存队列中抓取任务处理请求,数据库层订阅消息减库存,减库存成功的请求返回秒杀成功,失败的返回秒杀结束。

3.利用缓存应对读请求:对于读多写少业务,大部分请求是查询请求,所以可以读写分离,利用缓存分担数据库压力。

4.利用缓存应对写请求:缓存也是可以应对写请求的,可把数据库中的库存数据转移到Redis缓存中,所有减库存操作都在Redis中进行,然后再通过后台进程把Redis中的用户秒杀请求同步到数据库中。

4.2.4 数据库层
数据库层是最脆弱的一层,一般在应用设计时在上游就需要把请求拦截掉,数据库层只承担“能力范围内”的访问请求。所以,上面通过在服务层引入队列和缓存,让最底层的数据库高枕无忧。

如果不使用缓存来作为中间缓冲而是直接访问数据库的话,可以对数据库进行优化,减少数据库压力。

对于秒杀系统,直接访问数据库的话,存在一个【事务竞争优化】问题,可使用存储过程(或者触发器)等技术绑定操作,整个事务在MySQL端完成,把整个热点执行放在一个过程当中一次性完成,可以屏蔽掉网络延迟时间,减少行级锁持有时间,提高事务并发访问速度。

猜你喜欢

转载自blog.csdn.net/Jorocco/article/details/82184577