1:高并发系统:日PV在千万以上 2:衡量高并发常用的一些指标有 1:响应时间:是指系统对请求做出响应的时间,例如:系统处理一个请求需要100ms,这个100ms就是响应时间 2:吞吐量:是指单位时间内能处理的用户请求数量 3:QPS:是指系统每秒能够响应的查询次数,是对服务器在每秒内能处理多少流量的衡量标准 4:并发用户数:是指同时能承载用户正常使用系统功能的用户数量 3:高并发解决方案 1:硬件::提升服务器硬件配置,称为单体应用垂直扩容 2:缓存 3:集群 4:拆分 5:静态化 6:池化 7:动静分离 8:队列 9:java代码优化 10:数据库优化 11:Linux优化 12:压测 13:Nginx优化 14:网络优化 15:前端优化 4:单体应用垂直方案: 1:CPU从32为提升为64位 2:内存从64GB提升为256GB(比如缓存服务器) 3:磁盘HDD(Hard Disk Drive)提升为SSD(固态硬盘(Solid State Drives)),有大量读写的应用 4:磁盘扩容,1TB扩展到2TB,比如文件系统 5:千兆网卡提升为万兆网卡 硬件提升性能不可能永无止尽,所以最终还是要靠分布式解决 5:缓存 1:缓存可以说是解决大流量高并发,优化系统性能非常重要的一个策略 2:http缓存 1;浏览器缓存 2:Nginx缓存 3:CDN缓存 6:浏览器缓存 1:浏览器缓存是指当我们使用浏览器访问一些网站页面或者HTTP服务时,根据服务器端返回的缓存设置响应头将响应内容缓存到浏览器, 下次可以直接使用缓存内容或者仅需要去服务器端验证内容是否过期即可,这样可以减少浏览器缓存和服务器之间来回传输的数据量, 节省宽带,提升性能,比如新浪:http://www.sina.com.cn/ 第一次访问返回200,第二次刷新访问,返回响应码304,表示 页面内容没有修改过,浏览器缓存的内容还是最新的,不需要从服务器获取,直接读取浏览器缓存即可,我们也可以在java代码中 通过设置响应头,告诉前端浏览器进行缓存 response.addHeader("Date",format.format(new Date)); 设置过期时间 response.addHeader("Expires",format.format(new Date(now + 20 * 1000))); 文档生存时间 response.addHeader("Cache-Control","max-age=20"); 7:Nginx缓存 1:Nginx提供了expires指令来实现缓存控制,比如: location /static { root/opt/static/; expires 1d;//全天 } 当用户访问时,Nginx拦截到请求后先从Nginx本地缓存查询数据,如果有且没有过期,则直接返回缓存内容 8:CDN缓存 1:CDN的全称是Content Delivery Network,即内容分发网络,CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器, 通过中心平台的负载均衡,内容分发,调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率, CDN的关键技术主要有内容存储和分发技术 2:CDN本身也是一个缓存,他把后端应用的数据缓存起来,用户要求访问的时候,直接从CDN上获取,不需要走后端的Nginx,以及具体应用服务器 Tomcat,他的作用主要是加速数据的传输,也提高稳定性,如果从CDN上没有获取数据,再走后端的Nginx缓存,Nginx上也没有,则走后端应用 服务器,CDN主要缓存静态资源 9:应用缓存 1:内存缓存:在内存中缓存数据,效率高,速度快,应用重启缓存丢失 2:磁盘缓存:在磁盘缓存数据,读取效率较之内存缓存稍低,应用重启缓存不会丢失 2.1:代码组件:Guava,Ehcache 2.2:服务器:Redis,MemCache 3:多级缓存:在整个应用系统的不同层级进行数据的缓存,多层次缓存,来提升访问效率,比如:浏览器->CDN->Nginx->Redis->DB(磁盘,文件系统) 4:缓存的应用场景: 1:经常需要读取的数据 2:频繁访问的数据 3:热点数据缓存 4:IO瓶颈数据 5:计算昂贵的数据 6:无无需实时更新的数据 7:缓存的目的是减少对后端服务的访问,降低后端服务的压力 10:集群 有一个单体应用,当访问流量很大无法支撑,那么可以集群部署,也叫做单体应用水平扩容,原来通过部署一台服务器提供服务,现在就多部署几台,那么服务的能力就会提升。 部署了多台服务器,但是用户访问入口只能是一个,比如:www.web.com,所以就需要负载均衡,负载均衡是应用集群扩容后的必须步骤,集群部署后, 用户的会话session状态要保持的话,就需要实现session共享。 11:应用拆分:分布式,微服务 单体应用,随着业务的发展,应用功能的增加,单体应用就逐步变得非常庞大,很多人维护这么一个系统,开发,测试,上线都会造成很大的问题,比如:代码冲突,代码重复, 逻辑错综混乱,代码逻辑复杂度增加,响应新需求的速度降低,隐藏的风险增大,所以需要按照业务维度进行应用拆分,采用分布式开发。 应用拆分之后,就将原来在同一进程里的调用变成了远程方法调用,此时就需要使用到一些远程调用技术:httpClient,hessian,dubbo,webservice等。 随着业务复杂度增加,我们需要采用一些开源方案进行开发,提升开发和维护效率,比如Dubbo,SpringCloud。 通过应用拆分之后,扩容就变得容易,如果此时系统处理能力跟不上,只需要增加服务器即可(把拆分后的每一个服务再多做几个集群)。 12:数据库拆分:分为垂直拆分和水平拆分(分库分表) 按照业务维度把相同类型的表放在一个数据库,另一些表放在另一个数据库,这种方式的拆分叫垂直拆分,也就是在不同库创建不同表,把表分散到各个数据库。 比如产品,订单,用户三类数据以前在一个数据中,现在可以用三个数据库,分别为产品数据库,订单数据库,用户数据库。 这样可以将不同的数据库部署在不同的服务器上,提升单机容量和性能问题,也解决多个表之间的IO竞争问题。 根据数据行的特点和规则,将表中的某些行切分到一个数据库,而另外的某些行又切分到另一个数据库,这种方式的拆分叫水平拆分。 单库单表在数据量和流量增大的过程中,大表往往会成为性能瓶颈,所以数据库要进行水平拆分。 数据库拆分,采用一些开源方案,降低开发难度,比如:mycat,Sharding-Sphere。 13:静态化 对于一些访问量大,更新频率较低的数据,可直接定时生成静态html页面,供前端访问,而不是访问jsp。 常用静态化技术:freemaker,velocity。 定时任务,每隔2分钟生成一次首页的静态页面。 页面静态化首先可以大大提升访问的速度,不需要去访问数据库或者缓存来获取数据,浏览器直接加载html页面即可。 页面静态化可以提升网站的稳定性,如果程序或数据库出了问题,静态页面依然可以正常访问。 14:动静分离: 采用比如Nginx实现动静分离,Nginx负责代理静态资源,Tomcat负责处理动态资源 Nginx的效率极高,利用他处理静态资源,可以为后端服务器分担压力 redis和nginx并发量在4w左右,tomcat在800-1000,mysql极限在1400 15:队列 1:采用队列是解决高并发大流量的利器 2:队列的作用就是:异步处理,流量削峰,系统解耦 3:异步处理是使用队列的一个主要原因,比如注册成功了,发送优惠卷,送积分,送红包,发短信,发邮件等操作可以异步处理 4:使用队列流量削峰,比如并发下单,,秒杀等,可以考虑使用队列将请求暂时入队,通过队列的方式将流量削平,变成平缓请求进行处理,避免应用系统因瞬间的巨大压力而压垮 5:使用队列实现系统解耦,比如支付成功了,发消息通知物流系统,发票系统,库存系统等,而无需直接调用这些系统 6:队列应用场景 1:不是所有的处理都必须要实时处理 2:不是所有的请求都必须要实时告诉用户结果 3:不是所有的请求都必须100%一次性处理成功 4:不知道哪个系统需要协助来实现他的业务处理,保证最终一致性,不需要强一致性 7:常见的消息队列:ActiveMQ/RabbitMQ/RocketMQ/kafka 1:ActiveMQ是jms规范下的一个老牌的成熟的消息中间件/消息服务器 2:RabbitMQ/RocketMQ数据可靠性极好,性能也非常优秀,在一些金融领域,电商领域使用很广泛,RocketMQ是阿里巴巴的 3:kafka主要运用在大数据领域的,用于对数据的分析,日志的分析等处理,他有可能产生消息的丢失问题,他追求性能,性能极好,不追求数据的可靠性 16:池化:在实际开发中,我们经常会采用一些池化技术,减少资源消耗,提升系统性能 1:对象池:通过复用对象,减少对象的创建和垃圾收集器回收对象的资源开销,可以采用commons-pool2实现,实际项目采用对象并不常见,主要在开发框架或组件的时候会采用 2:数据库连接池:Druid/DBCP/C3P0/BoneCP 3:redis连接池:JedisPool(内部基于commons-pools实现) 4:HttpClient连接池:核心实现类:PoolingClientConnectionManager 5:线程池:java提供java.util.concurrent包可以实现线程池 1:Executors.newFixedThreadPool(8);线程数量固定 2:Executors.newSingleThreadExecutor();只有一个线程,避免关闭情况 3:Executors.newCachedThreadPool();可以自动扩容 4:Executors.newScheduledThreadPool(10);每隔多久执行 17:JVM优化: 1:-server JVM有两种运行模式Server与Client,两种模式的区别在于,Client模式启动速度快,Server模式启动速度慢, 但是启动进入稳定长期运行之后Server模式的程序运行速度比Client要快很多。 2:-Xmx2g 最大堆大小 3:-Xms2g 初始堆大小 4:-Xmn256m 堆中年轻代大小 5:-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64,由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。 6:-Xss 每个线程的Stack大小 7:-XX:+DisableExplicitGC,这个参数作用是禁止代码中显示调用GC,代码如何显示调用GC呢,通过System.gc()函数调用,如果加上了这个JVM 启动参数,那么代码中调用System.gc()没有任何效果,相当于是没有这行代码一样 8:-XX:+UseConcMarkSweepGC 并发标记清除(CMS)收集器。CMS收集器也被称为短暂停顿并发收集器 9:-XX:+CMSParallelRemarkEnabled 降低标记停顿 10:-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对老年代的压缩 11:-XX:LargePageSizeInBytes 指定java heap的分页页面大小 12:-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义的初始化定义开始CMS收集 13:-XX:CMSInitiatingOccupancyFraction 使用CMS作为垃圾回收使用70%后开始CMS收集 18:Tomcat优化: 1:设置JVM参数:在tomcat的bin目录下的catalina.sh中设置jvm参数。 1:JAVA_OPT$="-server -XX:+PrintGCDeatils -Xmx4g -Xms4g -Xmn256m -XX:PermSize=128m -Xss256k 2:-XX:+DisableExplicitGC 3:-XX:+UseConcMarkSweepGC 4:-XX:+CMSParallelRemarkEnabled 5:-XX:+UseCMSCompactAtFullCollection 6:-XX:LargePageSizeInBytes=128m 7:-XX:+UseFastAccessorMethods 8:-XX:+UseCMSInitiatingOccupancyOnly 9:-XX:CMSInitiatingOccupancyFraction=70 2:设置tomcat的线程池大小 3:设置IO模式 4:配置APR:可以参考文章:https://www.cnblogs.com/zhuawang/p/5213192/html 19:Java程序优化: 1:养成良好的编程习惯 2:不要重复创建太多对象 3:流/文件/连接 一定要记得在finally块中关闭 4:少用重量级同步锁synchronized,采用lock 5:不要在循环中使用try/catch 6:多定义局部变量,少定义成员变量 20:数据库优化: 1:数据库服务器优化:修改数据库服务器的配置文件的参数,偏DBA(数据库管理员) 2:数据库架构优化: 1:将数据库服务器和应用服务器分离 2:读写分离:通过数据库主从架构解决,写数据时操作主库,读数据时操作从库,分摊读写压力 3:分库分表:扩容数据库,解决数据量容量问题 3:数据库索引优化: 1:建立合适的索引 2:建立索引的字段尽量的小,最好是数值 3:尽量在唯一性高的字段上创建索引,主键,序号等 4:不要在性别这种唯一性很低的字段上创建索引 5:补充: 1:索引需要独立存放到同一个文件中,索引值在索引的结构中必须是有顺序的,BTREE索引最多只需要3次磁盘IO就能判断数据是否存放,这个数据在表中的哪一行。 2:索引最好使用数据类型,不要使用字符串,因为字符串更加浪费磁盘空间,而且字符串没有先后顺序没有大小,如果比对时需要将两个字符串中每一个字符逐一进行比对 才能判断出字符串是否相同因此效率很低。 3:索引最好具有唯一性,这样效率很高, 4:不要过多的建立索引,因为索引是需要独立存储的,每次更新数据时都需要额外的时间来更新索引,虽然索引可以提高查询效率,但是会降低更新效率 5:第三次磁盘IO获取叶子结点中所有的数据,这些数据包括索引值以及索引所对应的数据在表中的具体位置 6:聚簇索引(聚合索引,聚类索引):要求索引在索引结构中的位置顺序必须要与数据在表中的物理顺序位置完全一致,例如某个索引30在索引结构是第6个那么他所对应的数据 在表中也一定是第6个,一个表中最多只能有一个聚簇索引,主键是聚簇索引 7:主键不建议使用字符串更不要使用UUID,主键建议使用int或bigint,配合单调的自增(可以使用数据库自增也可以自己计算一个自增规则) 8:Hash索引:hash结构的索引也是有顺序的,他比较适合字符串作为索引,因为他的查找是根据Hash判断的,Hash在做等于查找时的他的效率是非常高的,但是不适合做区间查询, 综合效率没有BTREE要好。 9:位图索引:他使用图形结构来存放数据,如果重复率越高索引效率也就越高,如果重复率越低例如唯一值那么索引效率非常低,位图索引的存放空间比BTREE要更加大, 如果数据重复率越低磁盘空间也浪费。mysql不支持位图索引 4:SQL优化:参考文章:https://blog.csdn.net/jie_liang/article/details/77340905 5:采用数据搜索引擎:solr,elasticearch 21:Nginx优化:调整配置文件参数 1:worker processes 16; 与cpu相关 = cpu个数或者两倍 2:gzip on; 开启gzip压缩输出 3:events{ worker_connections 65535; 极限值65535 multi_accept on; 开启多路连接 use epoll; 使用epoll模型 } 22:前端优化: 1:js优化: 1:压缩变小,使用压缩工具 2:多个js合并成一个js文件,直接手动拷贝到一个文件中去,页面只加载这一个文件或者利用程序,比如controller,aa/js?path=xxx.js,xxx.js 2:css优化 1:压缩变小 2:多个css文件合并成一个css文件 3:html页面优化 1:不要加载太多的js和css 2:js和css加载放在页面的尾部,从用户体验角度考虑的 3:页面上减少到服务的请求数 23:压测 1:压测就是压力测试 2:在系统上线前,需要对系统各个环节进行压力测试,发现系统的瓶颈点,然后对系统的瓶颈点进行调优,调优完成后,还需要考虑另外一些风险因素, 比如网络不稳定,机房故障等,所以我们需要提前有故障预备方案,比如多机房部署容灾,路由切换等,故障预备方案做好后,还需奥提前进行演练, 以确保预案的有效性 3:压力测试工具:Apache JMeter/LoadRunner等 4:CTO,架构师,技术团队,测试团队,运维团队,DBA等共同完成。
高并发解决方案
最新推荐文章于 2024-05-04 12:01:34 发布