后端必备:如何排查问题以及jvm调优思路(一)
大家好,我是田螺。今天跟大家就CPU、磁盘、网络及内存方面的问题,聊聊如何排查和调优。
• CPU过高,怎么排查问题
• linux内存
• 磁盘IO
• 网络IO
• java 应用内存泄漏和频繁 GC
• java 线程问题排查
• 常用 jvm 启动参数调优
linux CPU 过高,怎么排查问题
CPU 指标解析
• 平均负载
◆ 平均负载等于逻辑 CPU 个数,表示每个 CPU 都恰好被充分利用。如果平均负载大于逻辑 CPU 个数,则负载比较重
• 进程上下文切换
◆ 无法获取资源而导致的自愿上下文切换
◆ 被系统强制调度导致的非自愿上下文切换
• CPU 使用率
◆ 用户 CPU 使用率,包括用户态 CPU 使用率(user)和低优先级用户态 CPU 使用率(nice),表示 CPU 在用户态运行的时间百分比。用户 CPU 使用率高,通常说明有应用程序比较繁忙
◆ 系统 CPU 使用率,表示 CPU 在内核态运行的时间百分比(不包括中断),系统 CPU 使用率高,说明内核比较繁忙
◆ 等待 I/O 的 CPU 使用率,通常也称为 iowait,表示等待 I/O 的时间百分比。iowait 高,说明系统与硬件设备的 I/O 交互时间比较长
◆ 软中断和硬中断的 CPU 使用率,分别表示内核调用软中断处理程序、硬中断处理程序的时间百分比。它们的使用率高,表明系统发生了大量的中断
查看系统的平均负载
$ uptime
10:54:52 up 1124 days, 16:31, 6 users, load average: 3.67, 2.13, 1.79
•10:54:52 是当前时间;up 1124 days, 16:31 是系统运行时间;6 users 则是正在登录用户数。而最后三个数字依次是过去 1 分钟、5 分钟、15 分钟的平均负载(Load Average)。平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数
•当平均负载高于 CPU 数量 70% 的时候,就应该分析排查负载高的问题。一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能
•平均负载与 CPU 使用率关系
◆CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的
◆I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高
◆大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高
CPU 上下文切换
•进程上下文切换:
◆进程的运行空间可以分为内核空间和用户空间,当代码发生系统调用时(访问受限制的资源),CPU 会发生上下文切换,系统调用结束时,CPU 则再从内核空间 ◆换回用户空间。一次系统调用,两次 CPU 上下文切换
◆系统平时会按一定的策略调用进程,会导致进程上下文切换
◆进程在阻塞等到访问资源时,也会发生上下文切换
◆进程通过睡眠函数挂起,会发生上下文切换
◆当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起
•线程上下文切换:
◆同一进程里的线程,它们共享相同的虚拟内存和全局变量资源,线程上下文切换时,这些资源不变
◆线程自己的私有数据,比如栈和寄存器等,需要在上下文切换时保存切换
•中断上下文切换:
◆为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件
查看系统的上下文切换情况:
vmstat 和 pidstat。vmvmstat 可查看系统总体的指标,pidstat则详细到每一个进程服务的指标
$ vmstat 2 1
procs --------memory--------- --swap-- --io--- -system-- ----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 3498472 315836 3819540 0 0 0 1 2 0 3 1 96 0 0
--------
cs(context switch)是每秒上下文切换的次数
in(interrupt)则是每秒中断的次数
r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数.当这个值超过了CPU数目,就会出现CPU瓶颈
b(Blocked)则是处于不可中断睡眠状态的进程数
# pidstat -w
Linux 3.10.0-862.el7.x86_64 (8f57ec39327b) 07/11/2021 _x86_64_ (6 CPU)
06:43:23 PM UID PID cswch/s nvcswch/s Command
06:43:23 PM 0 1 0.00 0.00 java
06:43:23 PM 0 102 0.00 0.00 bash
06:43:23 PM 0 150 0.00 0.00 pidstat
------各项指标解析---------------------------
PID 进程id
Cswch/s 每秒主动任务上下文切换数量
Nvcswch/s 每秒被动任务上下文切换数量。大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换
Command 进程执行命令
怎么排查 CPU 过高问题
•先使用 top 命令,查看系统相关指标。如需要按某指标排序则 使用 top -o 字段名 如:top -o %CPU。 -o 可以指定排序字段,顺序从大到小
# top -o %MEM
top - 18:20:27 up 26 days, 8:30, 2 users, load average: 0.04, 0.09, 0.13
Tasks: 168 total, 1 running, 167 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.3 us, 0.5 sy, 0.0 ni, 99.1 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem: 32762356 total, 14675196 used, 18087160 free, 884 buffers
KiB Swap: 2103292 total, 0 used, 2103292 free. 6580028 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2323 mysql 20 0 19.918g 4.538g 9404 S 0.333 14.52 352:51.44 mysqld
1260 root 20 0 7933492 1.173g 14004 S 0.333 3.753 58:20.74 java
1520 daemon 20 0 358140 3980 776 S 0.333 0.012 6:19.55 httpd
1503 root 20 0 69172 2240 1412 S 0.333 0.007 0:48.05 httpd
---------各项指标解析---------------------------------------------------
第一行统计信息区
18:20:27 当前时间
up 25 days, 17:29 系统运行时间,格式为时:分
1 user 当前登录用户数
load average: 0.04, 0.09, 0.13 系统负载,三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值
Tasks:进程相关信息
running 正在运行的进程数
sleeping 睡眠的进程数
stopped 停止的进程数
zombie 僵尸进程数
Cpu(s):CPU相关信息
%us:表示用户空间程序的cpu使用率(没有通过nice调度)
%sy:表示系统空间的cpu使用率,主要是内核程序
%ni:表示用户空间且通过nice调度过的程序的cpu使用率
%id:空闲cpu
%wa:cpu运行时在等待io的时间
%hi:cpu处理硬中断的数量
%si:cpu处理软中断的数量
Mem 内存信息
total 物理内存总量
used 使用的物理内存总量
free 空闲内存总量
buffers 用作内核缓存的内存量
Swap 内存信息
total 交换区总量
used 使用的交换区总量
free 空闲交换区总量
cached 缓冲的交换区总量
• 找到相关进程后,我们则可以使用 top -Hp pid 或 pidstat -t -p pid 命令查看进程具体线程使用 CPU 情况,从而找到具体的导致 CPU 高的线程
◆ %us 过高,则可以在对应 java 服务根据线程ID查看具体详情,是否存在死循环,或者长时间的阻塞调用。java 服务可以使用 jstack
◆ 如果是 %sy 过高,则先使用 strace 定位具体的系统调用,再定位是哪里的应用代码导致的
◆ 如果是 %si 过高,则可能是网络问题导致软中断频率飙高
◆ %wa 过高,则是频繁读写磁盘导致的。
linux 内存
查看内存使用情况
• 使用 top 或者 free、vmstat 命令
# top
top - 18:20:27 up 26 days, 8:30, 2 users, load average: 0.04, 0.09, 0.13
Tasks: 168 total, 1 running, 167 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.3 us, 0.5 sy, 0.0 ni, 99.1 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem: 32762356 total, 14675196 used, 18087160 free, 884 buffers
KiB Swap: 2103292 total, 0 used, 2103292 free. 6580028 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2323 mysql 20 0 19.918g 4.538g 9404 S 0.333 14.52 352:51.44 mysqld
1260 root 20 0 7933492 1.173g 14004 S 0.333 3.753 58:20.74 java
....
• bcc-tools 软件包里的 cachestat 和 cachetop、memleak
◆ achestat 可查看整个系统缓存的读写命中情况
◆ cachetop 可查看每个进程的缓存命中情况
◆ memleak 可以用检查 C、C++ 程序的内存泄漏问题
free 命令内存指标
# free -m
total used free shared buffers cached
Mem: 32107 30414 1692 0 1962 8489
-/+ buffers/cache: 19962 12144
Swap: 0 0 0
• shared 是共享内存的大小, 一般系统不会用到,总是0
• buffers/cache 是缓存和缓冲区的大小,buffers 是对原始磁盘块的缓存,cache 是从磁盘读取文件系统里文件的页缓存
• available 是新进程可用内存的大小
内存 swap 过高
Swap 其实就是把一块磁盘空间或者一个本地文件,当成内存来使用。swap 换出,把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存。swap 换入,在进程再次访问这些内存的时候,把它们从磁盘读到内存中来
• swap 和 内存回收的机制
◆ 内存的回收既包括了文件页(内存映射获取磁盘文件的页)又包括了匿名页(进程动态分配的内存)
◆ 对文件页的回收,可以直接回收缓存,或者把脏页写回磁盘后再回收
◆ 而对匿名页的回收,其实就是通过 Swap 机制,把它们写入磁盘后再释放内存
• swap 过高会造成严重的性能问题,页失效会导致频繁的页面在内存和磁盘之间交换
◆ 一般线上的服务器的内存都很大,可以禁用 swap
◆ 可以设置 /proc/sys/vm/min_free_kbytes,来调整系统定期回收内存的阈值,也可以设置 /proc/sys/vm/swappiness,来调整文件页和匿名页的回收倾向
linux 磁盘I/O 问题
文件系统和磁盘
• 磁盘是一个存储设备(确切地说是块设备),可以被划分为不同的磁盘分区。而在磁盘或者磁盘分区上,还可以再创建文件系统,并挂载到系统的某个目录中。系统就可以通过这个挂载目录来读写文件
• 磁盘是存储数据的块设备,也是文件系统的载体。所以,文件系统确实还是要通过磁盘,来保证数据的持久化存储
• 系统在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互。而在读写块设备文件时,会跳过文件系统,直接与磁盘交互
• linux 内存里的 Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。内核就可以把分散的写集中起来(优化磁盘的写入)
• linux 内存里的 Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读写的数据。下次访问这些文件数据时,则直接从内存中快速获取,而不再次访问磁盘
磁盘性能指标
• 使用率,是指磁盘处理 I/O 的时间百分比。过高的使用率(比如超过 80%),通常意味着磁盘 I/O 存在性能瓶颈。
• 饱和度,是指磁盘处理 I/O 的繁忙程度。过高的饱和度,意味着磁盘存在严重的性能瓶颈。当饱和度为 100% 时,磁盘无法接受新的 I/O 请求。
• IOPS(Input/Output Per Second),是指每秒的 I/O 请求数
• 吞吐量,是指每秒的 I/O 请求大小
• 响应时间,是指 I/O 请求从发出到收到响应的间隔时间
IO 过高怎么找问题,怎么调优
• 查看系统磁盘整体 I/O
# iostat -x -k -d 1 1
Linux 4.4.73-5-default (ceshi44) 2021年07月08日 _x86_64_ (40 CPU)
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.08 2.48 0.37 11.71 27.80 507.24 88.53 0.02 1.34 14.96 0.90 0.09 0.10
sdb 0.00 1.20 1.28 16.67 30.91 647.83 75.61 0.17 9.51 9.40 9.52 0.32 0.57
------
rrqm/s: 每秒对该设备的读请求被合并次数,文件系统会对读取同块(block)的请求进行合并
wrqm/s: 每秒对该设备的写请求被合并次数
r/s: 每秒完成的读次数
w/s: 每秒完成的写次数
rkB/s: 每秒读数据量(kB为单位)
wkB/s: 每秒写数据量(kB为单位)
avgrq-sz: 平均每次IO操作的数据量(扇区数为单位)
avgqu-sz: 平均等待处理的IO请求队列长度
await: 平均每次IO请求等待时间(包括等待时间和处理时间,毫秒为单位)
svctm: 平均每次IO请求的处理时间(毫秒为单位)
%util: 采用周期内用于IO操作的时间比率,即IO队列非空的时间比率
• 查看进程级别 I/O
# pidstat -d
Linux 3.10.0-862.el7.x86_64 (8f57ec39327b) 07/11/2021 _x86_64_ (6 CPU)
06:42:35 PM UID PID kB_rd/s kB_wr/s kB_ccwr/s Command
06:42:35 PM 0 1 1.05 0.00 0.00 java
06:42:35 PM 0 102 0.04 0.05 0.00 bash
------
kB_rd/s 每秒从磁盘读取的KB
kB_wr/s 每秒写入磁盘KB
kB_ccwr/s 任务取消的写入磁盘的KB。当任务截断脏的pagecache的时候会发生
Command 进程执行命令
• 当使用 pidstat -d 定位到哪个应用服务时,接下来则需要使用 strace 和 lsof 定位是哪些代码在读写磁盘里的哪些文件,导致IO高的原因
$ strace -p 18940
strace: Process 18940 attached
...
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0f7aee9000
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0f682e8000
write(3, "2018-12-05 15:23:01,709 - __main"..., 314572844
) = 314572844
munmap(0x7f0f682e8000, 314576896) = 0
write(3, "\n", 1) = 1
munmap(0x7f0f7aee9000, 314576896) = 0
close(3) = 0
stat("/tmp/logtest.txt.1", {st_mode=S_IFREG|0644, st_size=943718535, ...}) = 0
• strace 命令输出可以看到进程18940 正在往文件 /tmp/logtest.txt.1 写入300m
$ lsof -p 18940
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 18940 root cwd DIR 0,50 4096 1549389 /
…
java 18940 root 2u CHR 136,0 0t0 3 /dev/pts/0
java 18940 root 3w REG 8,1 117944320 303 /tmp/logtest.txt
----
FD 表示文件描述符号,TYPE 表示文件类型,NODE NAME 表示文件路径
• lsof 也可以看出进程18940 以每次 300MB 的速度往 /tmp/logtest.txt 写入