【后台开发必知必会】日常开发性能优化总结

“现代大规模关键性系统中的Java性能调优,是一项富有挑战的任务。需要关注各种问题,包括算法结构、内存分配模式及磁盘和文件I/O使用方式。性能调优最困难的是找出问题,即便是经验丰富的人也会被他们的自觉所误导。性能杀手总是隐藏在最意想不到的地方”

                                                                                 —James Gosling

                                                                               

性能优化

性能优化一般指降低响应时间提高系统吞吐量两个方面,但在流量高峰时期,服务的可用性才是应该关注的重点,此时性能优化也包括提高服务可用性。而以上提出的降低响应时间、提高系统吞吐量和提高服务可用性三者是相互矛盾的,不可兼得。比如增加缓存可以降低平均响应时间,但是处理线程数量会因为缓存过大而有所限制,从而降低系统吞吐量。所以需要在三者中找到平衡,实现业务需求。

「优化漏斗」是一个调优过程中的分层漏斗,由顶层到底层分别是应用程序层、框架层、JVM层、操作系统层。总体来说,层级越靠上,其调优的效果越明显,整体优化效果是自上而下衰减的。

如何量化性能,我们针对不同的端有以下性能指标:

  • Web端首屏时间、白屏时间、可交互时间、完全加载时间

首屏时间即用户打开网页开始到浏览器第一屏渲染完成的时间,一般说页卡就是指首屏时间长,用户体验不好。

首屏时间 = DNS时间 + 建立连接时间 + 后端响应时间 + 网络传输时间 + 首屏页面渲染时间

  • 移动端Crash率、内存使用率、FPS(FramesPer Second,每秒传输帧数)、端到端响应时间

端到端响应时间是衡量一个API性能的关键指标,比纯后端响应时间更全面,它会受到DNS、网络带宽、网络路由等多个因素的影响。

  • 后端: RT(Response Time,响应时间)、TPS(Throught Per Second,吞吐量)、并发数

后端系统响应时间是指系统对请求做出响应的时间(应用延迟时间),对于面向用户的web服务,应时间会受到数据库查询、RPC调用、网络IO、JVM垃圾回收等多反面因素的影响。对于高并发的应用和系统,吞吐量是关键指标,与Request对应的CPU、内存资源消耗、调用外部接口及IO紧密关联

下面主要侧重于后端的性能优化,提供以下几个方面值得思考:

代码

  1. for循环次数过多、递归调用导致栈溢出:是否可以在满足结果后break。
  2. 很多无谓条件判断:增加前置判断,不符合条件直接返回结果。
  3. 算法复杂度:是否有更优的算法,是否可优化当前算法。

业务逻辑优化

业务逻辑经常被忽略,但效果往往比数据库调优、JVM调优效果更明显。

举个栗子,淘宝中B端商家有个全部订单功能,如果是导出用户的全部订单,数据量过大导致加载过慢,可以更改业务逻辑,默认只会导出最近半年的订单,也通过在导出时增加时间段选择项,超时问题即可解决。

再举个栗子,12306抢票场景下,由于访问人数过多,用户点击“查票”之后系统会非常卡顿,作为用户会习惯再去点击“查票”,可能会连续点击好几次,假设每个人点击5次,后端系统负载会增加5倍,而其中80%的请求是重复请求。通过逻辑优化,将用户点击查询后将“查看”按钮置灰或通过JS控制xx秒内只能提交一次请求等,有效拦截80%的无效流量。

硬件升级

如机械硬盘升级为固态硬盘,提高服务器内存等

数据库

  1. SQL调优(频率较高):自带慢查询日志或开源慢查询系统定位出问题的SQL,然后explain、profile等工具逐步优化。也要注意包装好的底层SQL语句的合理性,如避免对查询条件计算、模糊查询LIKE使用避免%%、避免创建的索引失效等。
  2. 添加索引,加快数据的检索速度和表和表之间的连接,减少在使用分组和排序子句进行数据检索时间。常见的索引有唯一索引、主键索引、组合索引、聚集索引,也不是创建越多的索引越好,创建索引会占用资源空间而维护索引增加工作复杂度。
  3. 架构层面调优:读写分离:读数据操作一般都多于写操作,保证读可以任意扩展;多从库负载均衡,避免单个从库负载过高;水平和垂直分库分表:提升中心服务写入容量
  4. 连接池调优:为了数据库连接的高效获取、对数据库连接的限流,需要结合当前使用连接池的原理、具体的连接池监控数据和当前业务量作判断

缓存

 缓存分类:

  • 本地缓存:HashMap/ConcurrentHashMap、Guava Cache,适合数据量小,并且不会频繁增长又清空

  • 缓存服务:Redis、Memcache、Tair,适合数据量偏大,这几种具有不同的特点,比如Redis支持多种数据类型,Memcache全部存储在内存中。

使用缓存的目的:

  • 快速响应,减少响应时间

  • 防止数据库在高并发访问中不堪重负

优化关键点:

什么时候更新(删除)缓存?保障可靠性与实时性:

        A.设置缓存时间,一定时间后expire失效,重新从数据库加载数据,因此要容忍一定时间的数据不一致。

        B.选择缓存服务模型,同时更新缓存和数据库(Cache Aside);先更新缓存,缓存负责同步更新数据库(Read/Write Through);先更新缓存,缓存定时异步更新数据库(Write Behind                 Caching)。三种模式各有优势,根据业务场景选择使用,会带来更多系统开销和事务一致性问题。

缓存是否会满?满了使用什么策略?:

        A.LRU(Least Recently Used)最近最少使用原则,如在Redis中有6种淘汰策略allkeys-lru/random、volatile-lru/random/ttl、noeviction.

        B.到达一定容量(快满时)发出警报以扩容

        C.不同数据设置不同expire时间,使不常访问数据过期较快

缓存允许丢失吗?丢失了怎么办?

        根据业务要求设定,不允许丢失可以使用缓存服务的持久化功能,如redis的全量持久化(RDB)和增量持久化(AOF),丢失了可以加载已持久化的数据,但还是会有一定数据丢失(AOF会有一秒的丢失数据)

缓存击穿/雪崩?

         缓存服务一旦宕机或全部丢失,可能一瞬间所有的流量都直接打到了后 端数据库上,可能造成连锁反应,瞬间的请求高峰极有可能导致数据库无法承载。

        使用缓存对提高系统性能有很多好处,但是不合理的缓存使用会为系统造成负担,甚至风险。比如频繁修改得数据,没有热点的访问都是值得考虑的。

异步

使用异步的目的

  1. 缩短接口响应时间,使用户请求快速返回。
  2. 避免线程长时间处于运行状态,线程池可用线程减少,线程池任务队列长度增大,阻塞等多请求。
  3. 线程长时间处于运行状态会引起系统负载、CPU使用率、机器整体性能下降。

优化方式

  1. 开辟一个线程或线程池,如果线程处理的数据量很大,可以引入阻塞队列BlockingQueue,让一批异步线程不断往阻塞队列中扔数据,然后额外开辟一个线程从阻塞队列中拿数据,进行批处理。根据场景需求选择不同的阻塞队列,如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等
  2. 使用消息队列MQ中间件,如RocketMQ、ActiveMQ、RabbitMQ、Kafka等,有些消息不需要这个系统处理,将消息通过消息中间件传送到其他系统处理。
  3. 使用NIO,一切网络IO皆可异步:RPC框架、Servlet 3.0提供的异步技术、Apache HttpAsyncClient、缓存异步接口等。

JVM调优

关键指标如gc time、gc count、各个分代(Eden、S0、S1、Old、Perm)的内存大小变化、机器的Load值和CPU使用率、JVM的线程数等

常见调优方法:

  1. 高峰期CPU使用率和Load使用率偏高,观察一些JVM的thread count和gc count(主要是young gc count),如果这两个值都偏高,基本判定是young gc频率过高导致,可以通过适当增大young区大小或者占比来解决。
  2. 如果发生full gc和old cms gc非常频繁,诱发STW(stop the world)时间相应加长,导致接口响应变慢。可能是出现了“内存泄漏”即应该释放的内存空间没有被释放掉,解决办法是找到这些应该被释放的对象,可以使用jdk中提供的工具如jmap、MAT定位具体代码。
  3. 减少gc次数或单次gc时间,来降低整个应用的STW时间,以加快接口响应。

多线程与分布式

使用场景:IO任务、离线任务、异步任务、大数据任务、耗时较长任务

注意不是多线程就一定比单线程效率高,由于多线程情况下CPU还要花时间维护,CPU处理各线程的请求时在线程间切换也要花时间,用了有时反而会得不偿失。

  1. 单机多线程:通过引入线程池。更好管理线程,节省线程创建和销毁的开销,提高性能;限流,给线程池一个固定的容量,保障机器在极限压力下稳定处理的能力;根据业务场景设定调整核心参数如corePoolSize、maxPool Size、keepAliveTime、workerQueue等
  2. 多机多线程:多个节点组成一个分布式系统,协同工作,更加复杂。

服务器优化

  1. Nginx作为一个高性能的HTTP和反相代理服务器,常用作静态内容服务和负载均衡服务,提供更快、更可靠、单机支持10万以上并发连接的优势。如sendfile指令对于普通应用可设为on,IO重负载应用可设置为off,以平衡磁盘和网络IO处理速度;请求黑名单拦截、控制最大连接数、连接超时时间等设置都可以帮助更好优化服务性能。
  2. Tomcat应用服务器优化可提高网站的并发能力,运行方式的选择BIO、NIO、AIO,线程池属性设置如maxThreads、maxIdleTime、maxQueueSize、minSpareThreads,合理的设置能够使用最少的资源来调度;连接器作为Tomcat接受请求的入口,属性设置如protocol、enableLookups、asyncTimeout等。

小结:以上讲了很多性能优化可供参考的方向,从DNS连接到接入层再到逻辑层,存储由缓存到数据库,但不能盲目的去优化,最重要是找到性能瓶颈,再进行优化,在硬件、网络、资源一定的情况下以实现性能最大化。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值