性能调优——生产问题排查

原创2023-04-20 20:12·爱笑的程序猿小胡

一、CPU或内存使用率高

1、排查占用CPU的进程

1.1 top命令

  1. 按P键(大写打开时)会按照CPU使用率高低进行排序
  2. 按M键(大写打开时)会按照内存使用率高低进行排序

image-20230802082134339

Top 命令的输出信息主要分为两部分,上半部分显示 CPU 和内存资源的总体使用情况:

  • 第一行:系统当前时间,当前登录用户个数以及系统负载。
  • 第二行:系统总进程数、运行中进程数、休眠、睡眠和僵尸进程数量。
  • 第三行:CPU 当前使用情况。
  • 第四行:内存当前使用情况。
  • 第五行:Swap 空间当前使用情况。

1.2 jmap

jmap [options] pid
jmap -histo pid | head -20

1.3 jps

jps [options] [hostid]

option参数

参数 说明
-l 输出主类全名或jar路径
-q 只输出LVMID
-m 输出JVM启动时传递给main()的参数
-v 输出JVM启动时显示指定的JVM参数

其中 [option]、[hostid] 参数也可以不写。

image-20230802082246997

1.4 常用命令

  1. top:Linux 命令。可以实时查看各个进程的 CPU 使用情况。也可以查看最近一段时间的 CPU 使用情况。默认按 CPU 使用率排序。
  2. ps:Linux 命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前 CPU 使用情况。属于当前状态的采样数据。
  3. jstack:Java 提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码,以及是否死锁等等。
  4. pstack:Linux 命令。可以查看某个进程的当前线程栈运行情况。
  5. jstat:Java 提供的命令。可以查看某个JVM进程的gc信息。

2、解决问题实战

1、top

进到linux容器中执行 top命令,结果发现高cpu占用,其中pid 为1的正是我们的java程序。

image-20230802082337191

2、top -HP pid

执行命令找出占用CPU最高的线程 top -Hp pid 显示的新pid都是线程,找到占用cpu最高的线程号。

image-20230802082408477

3、printf "%x\n" tid

把线程号转换为16进制,便于在jstack中查询 printf "%x\n" tid 变为16进制。

image-20230802082434989

4、jstack pid | grep 0x十六进制结果 -A30

jstack命令查看相应线程的堆栈 jstack pid | grep 0x十六进制结果 -A30

image-20230802082516038

5、jstat -gcutil pid

命令查看进程的堆情况

image-20230802082547122

0:年轻代中第一个 survivor(幸存区)已使用的占当前容量百分比

S1:年轻代中第二个 survivor(幸存区)已使用的占当前容量百分比

E:年轻代中 Eden 区已使用的占当前容量百分比

O:老年代已使用的占当前容量百分比

M:元数据区使用比例

CCS:压缩使用比例

YGC:从应用程序启动到采样时年轻代中 gc 次数

YGCT:从应用程序启动到采样时年轻代中 gc 所用时间(单位秒)

FGC:从应用程序启动到采样时老年代 full gc 次数

FGCT:从应用程序启动到采样时老年代 full gc 所用时间(单位秒)

GCT:从应用程序启动到采样时 gc 用的总时间(单位秒)

6、jmap -dump:live,format=b,file=pid.hprof pid

命令导出堆文件,只导出 live 存活的对象。文件后缀名可以是任意的,因为它也是二进制的,不过通常以 hprof 结尾。

image-20230802082620028

7、最后使用 JDK 自带的工具,JAVA_HOME/bin/jvisualvm.exe工具分析快照

操作路径:文件 -> 装入 -> 文件类型选择堆,选中刚才导出的堆文件。

image-20230802082650127

选择类列表,按照大小排序,找出占用内存最大的类别

image-20230802082740477

二、数据库CPU使用率100%

1、QPS过高

1.1 首先,查看当前cpu曲线

image-20230802082819924

1.2 发现此时的cpu已经100%,再查看此时的qps曲线

image-20230802082842575

1.3 会发现此时的qps曲线基本和cpu曲线保持一致,此时我们可断定cpu飙升必然存在qps过高的原因。为了验证是否有慢sql的存在,再查看慢sql曲线

image-20230802082908380

发现此案例中完全不存在慢sql。因此责任可100%归为qps过高。

1.4 优化思路

  • 1.qps过高时,考虑是否可以使用缓存。
  • 2.使用批量操作,将多个操作合并为一次请求,但此种方式需要考虑是否可以一次批量的数据有多大,避免造成慢sql。
  • 3.考虑分库、读写分离,减少对一个机器的访问压力。
  • 4.机器升级,没什么是钱解决不了的。

2、慢查询

2.1 首先,查看当前cpu曲线

image-20230802082944405

2.2 然后查看qps曲线

image-20230802083003485

从上图我们可看出,cpu和qps的整体的整体走势是基本一致的,但是上图中相对qps曲线,cpu有好几次的抖动,甚至峰值达到80%,我们需要排查出这些峰刺点。

由于此时的cpu抖动和qps曲线不一致,可推测是慢sql引起的,观察下图抖动时间段内的慢sql,确定是否有慢sql,以及慢sql的具体信息。

观察上图发现该时间段内一些慢sql在库上使得cpu曲线发生了抖动,此时可采取kill+id的方法定制该sql的执行。

2.3 优化思路

  • 1.扫描数据库记录数较多。

考虑表是否设置了合理的索引,表字段是否设置了合理的数据类型,sql是否有效的利用了索引等。

  • 2.sql中是否有做了大量的聚合、计算?

考虑将sql简化,把逻辑操作上浮到业务中去做。

  • 3.sql返回的记录数过多。
    考虑分页实现,通过limit将一次请求转为多次请求。
  • 4.表中是否冗余字段过多?
    表若为宽表,包含大量冗余字段,可考虑分表。
  • 5.库中是否有很多张表?
    此时可考虑将表拆分到多个库中,分库。
  • 6.若库的读写较多,锁争抢激励,甚至死锁。
    可考虑多库做读写分离。
  • 7.机器的本身性能较低,不符合业务需求。
    可考虑机器升级了。

3、解决题实战

3.1 top查看mysql进程号或者ps -ef | grep -i mysql

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                  
16965 mysql     20   0   32.0g   2.8g  16592 S  1566  8.8  49:40.99 mysqld 

3.2 查看CPU使用率最高的线程号

top -Hp 16965

top -p 16965 -H

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                   
17053 mysql     20   0   32.0g   2.8g  16668 R 62.7  9.0   4:47.45 mysqld                                                                                    
17032 mysql     20   0   32.0g   2.8g  16668 R 61.7  9.0   5:03.16 mysqld                                                                                    
17028 mysql     20   0   32.0g   2.8g  16668 R 61.3  9.0   4:38.47 mysqld                                                                                    
17033 mysql     20   0   32.0g   2.8g  16668 R 61.0  9.0   4:56.53 mysqld                                                                                    
17054 mysql     20   0   32.0g   2.8g  16668 R 59.0  9.0   5:17.14 mysqld                                                                                    
17064 mysql     20   0   32.0g   2.8g  16668 R 59.0  9.0   4:57.18 mysqld                                                                                    
17057 mysql     20   0   32.0g   2.8g  16668 R 57.0  9.0   4:47.68 mysqld                                                                                    
17065 mysql     20   0   32.0g   2.8g  16668 R 56.7  9.0   5:00.24 mysqld 

可以看到占用最高的是PID=17053

3.3 找到对应的thread_id,processlist_id

mysql> select thread_id,name ,PROCESSLIST_ID,THREAD_OS_ID from performance_schema.threads where thread_os_id = 17053; 
+-----------+---------------------------+----------------+--------------+
| thread_id | name                      | PROCESSLIST_ID | THREAD_OS_ID |
+-----------+---------------------------+----------------+--------------+
|       143 | thread/sql/one_connection |             97 |        17053 |
+-----------+---------------------------+----------------+--------------+
1 row in set (0.00 sec)

3.4 找到当前占用cpu的SQL

elect DIGEST_TEXT  from performance_schema.events_statements_current where thread_id = 143 ;

+---------------------------------------------------------------------+
| DIGEST_TEXT
SELECT * FROM t xxx ……

三、补充

1、查看死锁

Mysql 查询是否存在锁表有多种方式,这里只介绍一种最常用的

1、查看正在进行中的事务
SELECT * FROM information_schema.INNODB_TRX

2、查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

3、查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

4、查询是否锁表
SHOW OPEN TABLES where In_use > 0;

5、查看最近死锁的日志
show engine innodb status

2、解除死锁

如果需要解除死锁,有一种最简单粗暴的方式,那就是找到进程id之后,直接干掉。

2.1 查看当前正在进行中的进程

show processlist
SELECT * FROM information_schema.INNODB_TRX;

2.2 杀掉进程对应的进程 id

kill id

2.3 验证(kill后再看是否还有锁)

SHOW OPEN TABLES where In_use > 0;
posted @ 2023-08-02 08:34  寻梦99  阅读(48)  评论(0)    收藏  举报