如何快速定位当前数据库消耗 CPU 最高的 sql 语句?

2024-06-12 09:26:54   2025-02-10 19:15:06   其他   234 views  

  mysql  

文章来自:PHP开源社区


如何快速定位消耗 CPU 最高的 sql 语句,怎么做?看看下面的介绍。

概述

如果是Oracle数据库我们可以很容易通过sql来定位到当前数据库中哪些消耗CPU高的语句,而mysql数据库可以怎么定位呢?这里用一个简单例子说明下...

主要是了解如何定位的思路,具体看官网介绍..

参考:https://www.percona.com/blog/2020/04/23/a-simple-approach-to-troubleshooting-high-cpu-in-mysql/

主要意思是针对定位CPU的问题,Percona增加了对通过信息的TID列将processlist ID映射到OS线程ID的支持,而MySQL在5.7版本后在PERFORMANCE_SCHEMA.THREADS表加了一个THREAD_OS_ID新列来实现,以下方法适用于在其他内核正常运行时,某个特定CPU的查询过载的情况。

find out which session is using the most CPU resources in my database?

定位线程

pidstat -t -p <mysqld_pid> 1  5

参数解释:

  • pidstat:命令本身,用于显示系统中各进程的统计信息。

    • -t:显示线程级别的统计信息。默认情况下,pidstat 只显示进程级别的信息,使用 -t 参数可以查看更详细的每个线程的信息。

    • -p :指定要监控的进程 ID(PID)。将 替换为你想要监控的 MySQL 进程的实际 PID。这个参数告诉 pidstat 只显示指定进程的信息。

    • 1:采样间隔时间,单位为秒。表示每 1 秒采集一次统计信息。

    • 5:采样次数。表示总共采集 5 次统计信息。

file

输出字段解释:

  • 09:31:02:时间戳,表示统计信息的采集时间。
  • TGID:线程组 ID(通常是进程 ID)。
  • TID:线程 ID。对于主线程,它与 TGID 相同,对于其他线程,它是独立的线程 ID。
  • %usr:在用户空间消耗的 CPU 百分比。
  • %system:在内核空间消耗的 CPU 百分比。
  • %guest:在虚拟化环境中运行客户系统消耗的 CPU 百分比。
  • %CPU:总的 CPU 使用百分比(%usr + %system + %guest)。
  • CPU:统计信息对应的 CPU 核心编号。
  • Command:进程或线程的名称。

通过该命令我们可以定位到802、4445等线程消耗了大量的CPU,这里尽量确保在pidstat的多个样本中验证消耗是恒定的。根据这些信息,我们可以登录到数据库,并使用以下查询找出哪个MySQL线程是罪魁祸首。

定位问题sql

select * from performance_schema.threads where thread_os_id = xx ;
select * from information_schema.`PROCESSLIST` where  id=threads.processlist_id

关于select * from performance_schema.threads where thread_os_id = xx ;
performance_schema.threads
performance_schema.threads是 MySQLPerformance Schema中的一个表。Performance Schema是一个用于监控 MySQL 服务器性能的工具,提供了对数据库服务器内部运行情况的详细监控和分析。threads表包含有关 MySQL 服务器中每个线程的详细信息。
thread_os_id
thread_os_id:这是threads表中的一个列,表示与操作系统相关的线程 ID。这通常是操作系统分配给 MySQL 线程的 ID,用于将 MySQL 线程与操作系统级别的线程对应起来。
输出字段解释:
THREAD_ID:MySQL 线程 ID。
NAME:线程的名称。
TYPE:线程的类型。
PROCESSLIST_ID:与 SHOW PROCESSLIST 中的 ID 对应。
PROCESSLIST_USER:执行线程的用户。
PROCESSLIST_HOST:执行线程的主机。
PROCESSLIST_DB:执行线程的数据库。
THREAD_OS_ID:操作系统线程 ID(即 thread_os_id)。
PROCESSLIST_COMMAND:线程正在执行的命令。
PROCESSLIST_TIME:线程已执行的时间。
PROCESSLIST_STATE:线程当前的状态。
PARENT_THREAD_ID:父线程 ID。
ROLE:线程的角色。

关于select * from information_schema.PROCESSLIST where  id=threads.processlist_id ;
threads.processlist_id为上一条语句结果中得出
information_schema.PROCESSLIST表:包含当前所有连接到 MySQL 服务器的会话信息。
id 列:唯一标识每个会话的 ID,等同SHOW PROCESSLIST命令中的 ID。
threads.processlist_id:在performance_schema.threads表中,每个线程processlist_id用于将线程与PROCESSLIST表中的会话关联起来。

file

根据操作系统id可以到processlist表找到对应的会话,如下:

file

查看问题sql执行计划

这里对应看一下执行计划基本就可以判断当前数据库CPU为什么消耗这么高了...

至于优化的点只需要在dock建一个索引即可,这里就不介绍了。

file

一般情况下建立复合索引并使用基本就可以了
优化两条语句,一条会全表扫描31w条另一条全表扫描29w条,加入复合索引后cpu从平均80%占用到峰值100%占用改善到平均10%占用峰值不超过60%目前还有8条语句依然走全表会造成瞬时cpu占用过高,还在优化中
优化前后对比

file