目录
一、系统优化指标
二、系统优化简介
三、系统优化
3.1 CPU 高
3.2 内存占用高
业务引起的内存升高
程序自身引起的内存问题
3.3 磁盘I/O
3.4 网络
3.5 数据库优化
3.6 响应时间高
3.7 吞吐量
3.8 代码层面优化
3.9 业务优化
四、JVM优化
4.1 堆内存设置
4.2 选择何时的垃圾收集器
4.3 其他方面的调优
在平时的工作中相信你一定做过系统优化吧,如果你还没做过,一直停留在简单的 CRUD 层面,那你要小心了...,这篇文章将带你了解系统调优的方法论及实际经验。
首先看一个案例,以前工作的公司,是金融方面的公司,每天的凌晨都有很多结算任务,其中一个定时任务跑的特别慢,随着数据的增长,每天大概需要跑4个多小时,严重的影响了后续业务的进行,急迫的需要进行优化。公司有个大佬任务,分析了半天时间,没改任何代码就是修改了一个配置,就让这个定时任务的执行时间缩短了1半,降到了两个多小时,是不是觉得很厉害?
一、系统优化指标
想要对系统进行优化首先要了解有哪些因素可以体现出系统存在的性能问题,主要有以下这些方面:
CPU:有的应用需要进行大量的计算,它们会长时间、不间断的占用 CPU 资源,导致其他资源无法获取 CPU 资源,从而影响其性能。
内存:内存是很重要的提升性能的利器,如果内存资源紧张,那应用程序无论如何也是快不起来的。
磁盘I/O:磁盘相比内存来说,存储空间要大很多,但磁盘 I/O 读写的速度要比内存慢,虽然目前引入的 SSD 固态硬盘已经有所优化,但仍然无法与内存的读写速度相提并论。
网络:网络对于系统性能来说,也起着至关重要的作用。如果你购买过云服务,一定经历过,选择网络带宽大小这一环节。带宽过低的话,对于传输数据比较大,或者是并发量比较大的系统,网络就很容易成为性能瓶颈。
数据库指标:大部分系统都会用到数据库,而数据库的操作往往是涉及到磁盘 IO 读写。大量的读写操作,会导致磁盘 IO 性能瓶颈, 进而导致数据库操作的延迟性,对于大量数据库操作读写来说,数据库的性能优化是整个系统的核心。
响应时间:响应时间是一个很重要的指标,响应时间越短,性能越好,一般的接口响应时间应该在 200ms 以内,超过 1s 的话,用户的感觉已经很明显了,用户体检较差。
吞吐量:我们需要关注系统的 QPS(每秒查询数) 和 TPS(每秒事务数),QPS 可以反应出系统在高并发场景下的处理能力,TPS 可以反应出系统处理复杂业务的能力。
二、系统优化简介
遇到系统优化有的人一上来就是 JVM 优化,相反 JVM 差不多快是系统优化的最后手段了,影响系统性能的因素有很多,如下图:
想做系统优化一个很重要的问题是首先要发现系统的瓶颈,那就需要完善的监控系统了。监控工具有很多开源的或者商用的监控,相信每个公司都会进行监控系统的接入,就不过多介绍,只要你能通过监控工具发现问题就行,下面来说一下如何进行系统优化。
三、系统优化
3.1 CPU 高
CPU 是很重要的监控指标,任何程序的运行都离不开 CPU,那 CPU 高了,我们要如何定位到时哪里引起的呢?
首先执行 top -c 显示进程运行列表信息。输入P(大写),进程按照CPU使用率排序。
如上图,最耗 CPU 的进程PID为 7。 执行top -Hp 7,显示一个进程的线程运行列表,输入P(大写),线程按照CPU使用率排序。
如上图,进程7内,最耗CPU的线程PID为174。产看堆栈,定位到线程,看看他在干嘛。首先将PID转为16进制:printf "%x\n" 174
之所以要转成16进制,是因为堆栈里,线程id是16进制表示的。查看堆栈:jstack 7 | grep '0x7b' -C5 --color
如上图找到了 CPU 高的线程对应的线程名称“http-nio-8080-exec-38”,以及看到了该线程正在执行的代码的堆栈。最后根据堆栈,找到对应的代码。
CPU 高的原因可能由以下几点
- 运算操作确实过多,CPU真实的高
- 大量的IO操作,CPU在等待IO,由于没有办法做切换,这使CPU虚高
- 程序问题,出现死循环等
3.2 内存占用高
关于内存引起的性能问题,通常由两个方面,一个是业务上使用不当,一个是程序可能存在内存泄漏或内存溢出。
业务引起的内存升高
先看一个例子,我们生产环境使用的 TiDB 分布式数据库,某一天突然出现了很多慢查询报警,然后就开始分析,通过监控发现,内存较之前升高了很多,用户量没有增长,那为何内存使用会突然增高呢?后来发现是TiCDC同步数据的问题。
TiDB server 和 TiCDC 是部署在同一台机器上的,表里的数据很多,然后新增字段对表进行了刷数据,这是 TiCDC 需要将数据同步到灾备库里,但是灾备库的性能很差,就导致数据长时间停留在 TiCDC 的内存中,TiDB server 和 TiCDC 又是部署在同一台机器上,间接导致 TiDB server 可用内存减少,这就导致了大量慢查询的产生,这种内存问题是使用问题,只要用心观察就会发现问题所在。
问题解决方案页简单,将 TiCDC 组件与 TiDB server 分开部署,同时升级灾备库的硬件,这个问题就解决了。
程序自身引起的内存问题
在来看程序使用的问题,如果 JVM 无法回收你的对象,时间长了就会产生内存泄漏,比如不签当使用 ThreadLocal 没有调用 remove 方法,从而引起对象无法回收,长期占用内存资源。
内存溢出则是在程序运行过程中,试图申请的内存超过了系统所能分配的最大内存空间,或者超过了当前剩余可用的内存空间,导致系统无法满足程序的内存需求。一旦发生内存溢出,程序往往会抛出异常(如Java中的OutOfMemoryError
),并可能导致程序终止运行。
如果 JVM 设置的堆内存较小,而一次又加载大量数据,就会引起内存溢出问题。
3.3 磁盘I/O
磁盘 I/O 高代表磁盘的读写压力很大,通常可以升级磁盘硬件性能,或者增加磁盘数量来分散压力等方式即可解决。
3.4 网络
网络使用过高指的是网络带宽占用过高或网络资源消耗过大,导致网络性能下降、网络延迟增加或网络服务质量受到影响。
定位问题:使用网络监控工具(如NetTop、Wireshark、iftop、nethogs等)来实时监测网络流量,找出占用带宽最大的进程或服务。
然后要减少必要的请求,使用缓存减少请求等方式来减少交互,或者通过升级网络带宽的方式来改善该问题。
3.5 数据库优化
数据库是很多系统必不可少的组件,可以通过监控慢 SQL或其他监控 来进一步确定优化的方向。关于 MySQL 的相关的问题,欢迎查阅往期文章。
3.6 响应时间高
接口的响应时间高有很多影响因素,比如依赖三方服务卡顿、数据库问题、当时网络问题、同步操作大量数据、缓存失效等等。
提升接口的响应时间方案有很多,比如找到借口的瓶颈点后进行相应的优化、并行处理、优化数据库、异步处理、增加缓存等等方式。
3.7 吞吐量
提高吞吐量的方式主要包括以下几点:
- 提升服务器硬件性能
- 使用缓存技术
- 数据库层面优化,减少慢查询,长事务等
- 代码层面,优化数据接口,减少 I/O 操作、使用多线程、合理设置 JVM 参数等
- 架构层面,采用无模式的水平扩容,增加服务机器
- 使用消息中间件来进行服务解耦,实现异步处理,增加吞吐量
3.8 代码层面优化
正确的使用代码是提高服务性能的基本条件,这里列举一些能提升代码性能的使用规范
- 使用 String 时,尽量复用字符串,少创建新对象,比如使用 intern
- 合理使用 ArrayList 和 LinkedList,注意其使用场景
- 尽量避免使用锁,减少不要的竞争和上下文切换
- 使用合理的 I/O 模型
- 避免磁盘随机读写
- 涉及到文件操作时,使用零拷贝
- 使用缓存减少查询数据库
- 需要序列化时避免使用 Java 序列化
3.9 业务优化
其实除了技术方面上的优化,业务上也有很多改进点,比如电商中的上面详情页,这个页面很重要,是关系到用户购买的关键一环,通常会在详情页上方优惠券等,来提高用户的购买意愿。但是页面加载的越多,意味着性能越差,可以把不想关的东西迁移到其他地方,比如优惠券可以在用户下单后再订单总展示。
同样的例子有很多,APP 的首页中不要放不想关的东西,一次来提升关键点的性能。
四、JVM优化
VM(Java虚拟机)调优主要是为了提高Java应用程序的性能,减少内存溢出、提高响应速度、优化资源利用效率等问题。
4.1 堆内存设置
- 调整 -Xms(初始堆大小)和 -Xmx(最大堆大小),避免频繁的堆空间动态扩展带来的性能损耗。
- 设置 -XX:NewRatio 控制年轻代和老年代的比例,优化 GC 策略。
- 考虑使用 -XX:MetaspaceSize、-XX:MaxMetaspaceSize 等设置元空间大小。
4.2 选择何时的垃圾收集器
根据应用特点选择合适的垃圾回收器(如Serial、Parallel、CMS、G1、ZGC、Shenandoah等),并对其相关参数进行调优。
4.3 其他方面的调优
- 设置合理的Survivor区比例(-XX:SurvivorRatio)和Eden区大小(与年轻代大小相关联)。
- 考虑是否开启压缩类空间指针(-XX:+UseCompressedOops)。
- 考虑是否开启偏向锁、轻量级锁等优化(-XX:+UseBiasedLocking,-XX:+UseFastAccessorMethods)。
总结:系统调优是一个很复杂的系统工程,平时要多注意观察学习,总结经验。生产环境通常情况下要经过压测才能找到系统的瓶颈,才能知道系统达到什么程度会有性能问题,做到心中有数,提前规划。
往期经典推荐
即时编译器在JVM调优战场的决胜策略-CSDN博客
JVM垃圾收集器你会选择吗?-CSDN博客
MySQL为什么会选错索引-CSDN博客
Redis使用规范的最佳实践:打造高性能与稳定性的关键法则-CSDN博客
SpringBoot项目并发处理大揭秘,你知道它到底能应对多少请求洪峰?_一个springboot能支持多少并发-CSDN博客
TiDB内核解密:揭秘其底层KV存储引擎如何玩转键值对_tidb 的key value是如何做到的-CSDN博客