程序优化基础知识
影响程序性能的服务器指标: 内存 CPU 磁盘大小、以及磁盘IO 网络带宽。
压测不得不说的命令top
第一行,任务队列信息,同 uptime 命令的执行结果
系统时间:19:24:30 运行时间:125天 1小时 30 min, 当前登录用户: 2 user load average: 0.22, 0.30, 0.40 average后面的三个数分别是1分钟、5分钟、15分钟的负载情况。 load average数据是每隔5秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。 这个数/(除以)逻辑CPU的数量反映出服务器的运行情况: 在 0.00 和 1.00 之间是通畅状态,理想状态是 0.70左右 load = 1表示CPU所有的资源都在处理请求,没有剩余的资源可以利用了。 load >= 2则表示CPU已经超负荷运作,另外还有一倍的线程正在等待处理。 load >= 5: 系统在超负荷运转了,服务随时是会挂掉。
第二行,Tasks — 任务(进程)
总进程:242 total, 运行:1 running, 休眠:241 sleeping, 停止: 0 stopped, 僵尸进程: 0 zombie
第三行,cpu状态信息
1.1%us【user space】— 用户空间占用CPU的百分比。
进程在用户地址空间中消耗 CPU 时间的百分比。像 shell程序、各种语言的编译器、数据库应用、web 服务器和JAVA服务都算是运行在用户地址空间的进程,绝大多数的 CPU 时间都是运行在用户空间。
0.3%sy【sysctl】— 内核空间占用CPU的百分比。
进程在内核地址空间中消耗 CPU 时间的百分比。所有进程要使用的系统资源都是由 Linux 内核处理的。当处于用户态(用户地址空间)的进程需要使用系统的资源时,比如:JAVA服务需要分配一些内存、或是执行 IO 操作。有或者一个程序创建一个子进程,此时就会进入内核态(内核地址空间)运行。事实上,决定进程在下一时刻是否会被运行的进程调度程序就运行在内核态。消耗在内核态的时间应该是越少越好。在实践中有一类典型的情况会使 sy 变大,那就是大量的 IO 操作,因此在调查 IO 相关的问题时需要着重关注它,比如:JAVA服务的日志打印。
0.0%ni【nice 】— 改变过优先级的进程占用CPU的百分比 。
可以通过 nice 值调整进程用户态的优先级。这里显示的 ni 表示调整过 nice 值的进程消耗掉的 CPU 时间。如果系统中没有进程被调整过 nice 值,那么 ni 就显示为 0。 假设在一个CPU轮转中,有2个runnable的进程A和B,如果他们的nice值都为0,假设内核会给他们每人分配1k个cpu时间片。但是假设进程A的为0,但是B的值为-10,那么此时CPU可能分别给A和B分配1k和1.5k的时间片。故可以形象的理解为,nice的值影响了内核分配给进程的cpu时间片的多少,时间片越多的进程,其优先级越高,其优先级值(PRI)越低。 ni,就是改变过优先级的进程的占用CPU的百分比,如上例中就是0.5k/2.5k=1/5=20%。 如果有人改动了相应的优先级导致其它用户分到的cpu时间片过低导致。 此时可以更改进程优先级来加速进程运行。 nice 和 renice 两个命令,可以通过 man nice查看,范围:大(-20~19)小 renice -15 -p 16666,把进程号为16666的进程优先级提高。 注意:系统允许root用户设置负数优先级,以及减小现有进程的优先级数值大小。对普通用户仅允许设置正数优先级,并且只能增大现有进程的优先级数值大小。
98.6%id【idle】— 空闲CPU百分比 CPU 处于 idle 状态的百分比。
一般情况下, us + ni + id 应该接近 100%。 所以,看CPU使用率的时候要看 id 空闲多少。
0.0%wa【wait】— IO等待占用CPU的百分比 。
CPU 等待磁盘 IO 操作的时间与 CPU 的处理速度相比,磁盘 IO 操作是非常慢的。 有很多这样的操作,比如:CPU 在启动一个磁盘读写操作后,需要等待磁盘读写操作的结果。 在磁盘读写操作完成前,CPU 只能处于空闲状态。 Linux 系统在计算系统平均负载时会把 CPU 等待 IO 操作的时间也计算进去,所以在我们看到系统平均负载过高时,可以通过 wa 来判断系统的性能瓶颈是不是过多的 IO 操作造成的。 可以通过iostat 命令查看硬盘和cpu的使用情况。
0.0%hi【Hardware Interrupts】— 硬中断占用CPU的百分比 。
硬中断是硬盘、网卡等硬件设备发送给 CPU 的中断消息,当 CPU 收到中断消息后需要进行适当的处理(消耗 CPU 时间)。
0.0%si【Software Interrupts】— 软中断占用CPU的百分比 。
软中断是由程序发出的中断,最终也会执行相应的处理程序(消耗 CPU 时间)。
0.0%st 【Steal Time】虚拟机占用百分比 。
只有 Linux 在作为虚拟机运行时 st 才是有意义的。 它表示虚机等待 CPU 资源的时间(虚拟机分到的是虚拟 CPU,当需要真实的 CPU 时,可能真实的 CPU 正在运行其它虚机的任务,所以需要等待)。 steal值比较高的话,说明服务器上的另一个虚拟机可能拥有更大更多的 CPU 时间片,你可能需要申请升级以与之竞争。 低 steal 值意味着你的应用程序在目前的虚拟机上运作良好。因为你的虚拟机不会经常地为了 CPU 时间与其它虚拟机激烈竞争,你的虚拟机会更快地响应。也说明主机没有运行过量的虚拟服务。
第四行,内存状态
KiB Mem : 32778460 total, 862056 free, 22090420 used, 9825984 buff/cache
【缓存的内存量】 可用内存=free + buffer/cached cached是cpu与内存间的,buffer是内存与磁盘间的,都是为了解决速度不对等的问题。
cached 是给读取数据时加速的,buffers 是给写入数据加速的。 cached 是指把读取出来的数据保存在内存中,当再次读取时,不用读取硬盘而直接从内存中读取,加速了数据的读取过程。 buffers 是指在写入数据时,先把分散的写入操作保存到内存中,当达到一定程度后再集中写入硬盘,减少了磁盘碎片和硬盘的反复寻道,加速了数据的写入过程。 回头说速度不对等。 磁盘:寻址:ms 毫秒 机械硬盘大概是100~150MB/s的水平,内存寻址时间:6ns左右(1ms=1000000ns) 内存:寻址:ns 纳秒 DDR4内存大概是60GB/s的水平,机械硬盘寻址时间:1/7200 ≈0.14ms 秒>毫秒>微妙>纳秒,磁盘比内存慢了大概100万倍,实际上差 4 个数量级左右。
第四行中使用中的内存总量(used)指的是现在系统内核控制的内存数, 第四行中空闲内存总量(free)是内核还未纳入其管控范围的数量。 纳入内核管理的内存不见得都在使用中,还包括过去使用过的现在可以被重复利用的内存,内核并不把这些可被重新使用的内存交还到free中去,因此在linux上free内存会越来越少,但不用为此担心。
第五行,swap交换分区信息
KiB Swap: 0 total, 0 free, 0 used. 9802736 avail Mem
【缓冲的交换区总量】 对于内存监控,在top里我们要时刻监控第五行swap交换分区的used,如果这个数值在不断的变化,说明内核在不断进行内存和swap的数据交换,这是真正的内存不够用了。
Swap分区,即交换区,Swap空间的作用:当物理内存不足时,将内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap空间中,等到那些程序要运行时,再从Swap中恢复保存的数据到内存中,实际上是规划出来的一块硬盘空间。
第六行,空行
第七行以下:各进程(任务)的状态监控
PID — 进程id
USER — 进程所有者
PR — 进程优先级
NI — nice值。负值表示高优先级,正值表示低优先级
VIRT — 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
RES — 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
SHR — 共享内存大小,单位kb
S —进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程
%CPU — 上次更新到现在的CPU时间占用百分比
%MEM — 进程使用的物理内存百分比
TIME+ — 进程使用的CPU时间总计,单位1/100秒
COMMAND — 进程名称(命令名/命令行)
top 命令的几个常用参数:
按数字“1”可监控每个逻辑CPU的状况。 c:切换显示命令名称和完整命令行。 i:忽略闲置和僵死进程。这是一个开关式命令。 f或者F:从当前显示中添加或者删除项目。 top -H -p 进程号,显示一个进程的线程信息。
压测前的准备工作
1、调整日志级别,去除频繁打印的日志。
从info或者debug调整到warn,原因:消耗系统资源,硬盘很快会满。
2、必须明确看到处理的数据量和速度。
从程序入口收集qps信息
public static AtomicLong mqQuantity = new AtomicLong(0); SmsBusinessConfig.mqQuantity.incrementAndGet(); new Thread(){ log.warn("接受MQ的qps:"+(mqQuantity.get()/60.0)); mqQuantity.set(0L); Thread.sleep(60*1000); }
打印重要处理环节的处理时间,内存队列的数据量。 比如:在一个入库线程中,打印线程总耗时,和入库耗时。
3、对中间件的配置和运行情况有一定了解。
比如:rocketMq及其控制台,参数配置,性能瓶颈。
4、压测前看一眼服务的资源占用情况。
开始压测
1、Apache JMeter配置适当的参数执行。
可以先用一个线程执行,得到单线程执行的qps,在根据自己想要的qps设置线程数。
2、开始阶段集中注意力,关注服务器和中间件的负载情况。
先得到一个程序当前的最大qps
3、有些问题短时间压测很难发现,所以需要长时间压测。
1小时、3小时、10小时、一天、三天压测。
4、程序出问题后收集信息,为优化程序提供依据。
jmap 堆快照 jmap -dump:file=/data/202107170910.dump 进程号 jstack 线程快照 jstack -l 进程号 > /data/jstack202107170917.log jstat 垃圾回收状态 jstat -gc 进程号 250 5 > /data/jstat202107170917.log 采样时间间隔为250ms,采样数为5
总结经验优化程序
单核cpu设定多线程是否有意义?
有意义,一个线程等待资源,另一个线程可以执行,不至于阻塞。 线程分两种:cpu密集型线程,对cpu的利用比较高。IO密集型,大量时间等待输入输出,等到数据很可能做一些简单拷贝,消耗CPU很小。大多数线程既有CPU也有IO。 java while true 空跑一个线程,会占用一个CPU的100%。
工作线程数是不是设置的越多越好?
当然不是,线程切换也是要消耗资源的,线程多了CPU都消耗在了线程切换上了。
工作线程数(线程池中的线程数)设置多少最合适呢?
1、推算,加上压测。
2、根据CPU的核数,cpu数个线程是个参考数字。
一台机器上还有很多程序占用CPU资源。看服务器的实际情况。从服务安全的角度考虑,不能让每个cpu跑到100%,跑到80%就可以了。
3、《java并发编程实践》 中的线程公式。
追问:怎么知道我程序的W/C是多少?
通过工具来进行测算,profile性能分析工具,Jprofile好用的收费工具。 部署上去看,一个线程空跑一个CPU是100%,CPU跑到50%就算时间大概是1/1。 实际运行和压测不一致,用工具 Arthas,阿里的阿尔萨斯工具查。
系统压测过程中遇到的问题?
测试的时候墨菲定律生效,你觉得可能出问题的地方都会出问题,具体以后补充。