从 Percona Server for MySQL 5.6.27 开始的 information_schema.processlist 表的列 TID 将进程列表 ID 映射到操作系统线程 ID 的支持。随着 5.7 的发布,MySQL 通过扩展 PERFORMANCE_SCHEMA.THREADS 表并添加一个名为 THREAD_OS_ID 的新列来跟随其自己的实现,Percona Server for MySQL 采用该列代替自己的列,因为它通常会尽可能靠近上游.

以下方法对于查询使一个特定 CPU 过载而其他内核正常执行的情况很有用。对于一般 CPU 使用率问题的情况,可以使用不同的方法,例如另一篇博客文章 Reducing High CPU on MySQL: A Case Study 中的方法

我们如何使用这个新列来找出哪个会话使用了数据库中最多的CPU资源?

让我们举个例子:

要解决CPU问题,我们可以使用几种工具,例如top或pidstat(需要sysstat程序包)。 在下面的示例中,我们将使用pidstat。 该工具有一个选项(-t),可将其视图从进程(默认值)更改为线程,在其中显示给定进程内的关联线程。 我们可以使用它来找出哪个线程在服务器中消耗了最多的CPU。 将-p参数与mysql进程ID一起添加,以便该工具仅显示MySQL线程,从而使我们更容易进行故障排除。 最后一个参数(1)是每秒显示一个样本:

命令为pidstat -t -p <mysqld_pid> 1

shell> pidstat -t -p 31258 1
03:31:06 PM   UID      TGID       TID    %usr %system  %guest    %CPU   CPU  Command[...]03:31:07 PM 10014         -     32039    5.00    1.00    0.00    6.00    22  |__mysqld
03:31:07 PM 10014         -     32040    5.00    1.00    0.00    6.00    23  |__mysqld
03:31:07 PM 10014         -     32042    6.00    1.00    0.00    7.00     8  |__mysqld
03:31:07 PM 10014         -     32047    5.00    1.00    0.00    6.00     6  |__mysqld
03:31:07 PM 10014         -     32048    5.00    1.00    0.00    6.00    15  |__mysqld
03:31:07 PM 10014         -     32049    5.00    1.00    0.00    6.00    14  |__mysqld
03:31:07 PM 10014         -     32052    5.00    1.00    0.00    6.00    14  |__mysqld
03:31:07 PM 10014         -     32053   94.00    0.00    0.00   94.00     9  |__mysqld
03:31:07 PM 10014         -     32055    4.00    1.00    0.00    5.00    10  |__mysqld
03:31:07 PM 10014         -      4275    5.00    1.00    0.00    6.00    10  |__mysqld
03:31:07 PM 10014         -      4276    5.00    1.00    0.00    6.00     7  |__mysqld
03:31:07 PM 10014         -      4277    6.00    1.00    0.00    7.00    15  |__mysqld
03:31:07 PM 10014         -      4278    5.00    1.00    0.00    6.00    18  |__mysqld
03:31:07 PM 10014         -      4279    5.00    1.00    0.00    6.00    10  |__mysqld
03:31:07 PM 10014         -      4280    5.00    1.00    0.00    6.00    12  |__mysqld
03:31:07 PM 10014         -      4281    5.00    1.00    0.00    6.00    11  |__mysqld
03:31:07 PM 10014         -      4282    4.00    1.00    0.00    5.00     2  |__mysqld
03:31:07 PM 10014         -     35261    0.00    0.00    0.00    0.00     4  |__mysqld
03:31:07 PM 10014         -     36153    0.00    0.00    0.00    0.00     5  |__mysqld12345678910111213141516171819202122

我们可以看到线程32053在很大程度上消耗了最多的CPU,并且我们确保验证了在pidstat的多个样本之间的消耗是否恒定。 利用这些信息,我们可以登录数据库,并使用以下查询来找出哪个MySQL Thread是罪魁祸首:

mysql > select * from performance_schema.threads where THREAD_OS_ID = 32053 G
*************************** 1. row ***************************
          THREAD_ID: 686
               NAME: thread/sql/one_connection
               TYPE: FOREGROUND
     PROCESSLIST_ID: 590
   PROCESSLIST_USER: msandbox
   PROCESSLIST_HOST: localhost
     PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Query
   PROCESSLIST_TIME: 0
  PROCESSLIST_STATE: Sending data
   PROCESSLIST_INFO: select * from test.joinit where b = 'a a eveniet ut.'
   PARENT_THREAD_ID: NULL
               ROLE: NULL
       INSTRUMENTED: YES
            HISTORY: YES
    CONNECTION_TYPE: SSL/TLS
       THREAD_OS_ID: 32053
1 row in set (0.00 sec)1234567891011121314151617181920

好了! 现在我们知道,CPU高消耗来自joinit表中的查询,该查询由数据库测试中来自本地主机的用户msandbox执行。 使用此信息,我们可以对查询进行故障排除,并使用EXPLAIN命令检查执行计划,以查看是否还有任何改进的余地。

mysql > explain select * from test.joinit where b = 'a a eveniet ut.' G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: joinit
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 7170836
     filtered: 10.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)123456789101112131415

In this case, it was a simple index that was missing!

mysql > alter table test.joinit add index (b) ;
Query OK, 0 rows affected (15.18 sec)
Records: 0  Duplicates: 0  Warnings: 0123

After we create the index, we are no longer seeing CPU spikes:

shell> pidstat -t -p 31258 1
03:37:53 PM   UID      TGID       TID    %usr %system  %guest    %CPU   CPU  Command[...]03:37:54 PM 10014         -     32039   25.00    6.00    0.00   31.00     0  |__mysqld
03:37:54 PM 10014         -     32040   25.00    5.00    0.00   30.00    21  |__mysqld
03:37:54 PM 10014         -     32042   25.00    6.00    0.00   31.00    20  |__mysqld
03:37:54 PM 10014         -     32047   25.00    4.00    0.00   29.00    23  |__mysqld
03:37:54 PM 10014         -     32048   25.00    7.00    0.00   32.00    22  |__mysqld
03:37:54 PM 10014         -     32049   23.00    6.00    0.00   29.00     4  |__mysqld
03:37:54 PM 10014         -     32052   23.00    7.00    0.00   30.00    14  |__mysqld
03:37:54 PM 10014         -     32053   10.00    2.00    0.00   12.00    11  |__mysqld
03:37:54 PM 10014         -     32055   24.00    6.00    0.00   30.00     1  |__mysqld
03:37:54 PM 10014         -      4275   25.00    6.00    0.00   31.00     7  |__mysqld
03:37:54 PM 10014         -      4276   25.00    6.00    0.00   31.00     1  |__mysqld
03:37:54 PM 10014         -      4277   24.00    5.00    0.00   29.00    14  |__mysqld
03:37:54 PM 10014         -      4278   24.00    6.00    0.00   30.00     9  |__mysqld
03:37:54 PM 10014         -      4279   25.00    5.00    0.00   30.00     6  |__mysqld
03:37:54 PM 10014         -      4280   26.00    5.00    0.00   31.00    14  |__mysqld
03:37:54 PM 10014         -      4281   24.00    6.00    0.00   30.00    10  |__mysqld
03:37:54 PM 10014         -      4282   25.00    6.00    0.00   31.00    10  |__mysqld
03:37:54 PM 10014         -     35261    0.00    0.00    0.00    0.00     4  |__mysqld
03:37:54 PM 10014         -     36153    0.00    0.00    0.00    0.00     5  |__mysqld12345678910111213141516171819202122

为什么不使用这种方法来解决IO和内存问题?

从OS端测量线程IO的问题在于,大多数MySQL IO操作是由后台线程完成的,例如读取,写入和页面清理程序线程。 要测量线程IO,您可以使用带有-d(IO而不是CPU)选项的pidstat或带有-H(每个线程)的iostat之类的工具。 如果您有一个非常消耗IO的线程,您也许可以看到它,但是要警告,由于后台线程操作,结果可能会产生误导。

内存消耗是要从OS端衡量的一个更加棘手的资源,因为所有内存都是在MySQL进程下分配的,并且由于是MySQL管理其内存访问,因此对于OS来说哪个线程消耗最多的内存是透明的。 为此,我们可以使用从5.7开始使用perfomance_schema memory instrumentation available starting in 5.7.

结论

有很多方法可以解决CPU使用率过高的问题,但是从5.7版开始,我们在Oracle和PostgreSQL数据库上提供了一种简单且广泛使用的方法,该方法可以适应MySQL。 通过从OS线程消耗跟踪到数据库端,我们可以快速检测到影响系统性能的CPU密集型查询。


正确理解数据库性能问题的真正原因可以快速有效地解决问题——但企业往往缺乏这种关键信息。没有它,您的解决方案可能需要比必要更多的时间和资源,或者无法有效地解决问题。与流行的看法相反,问题并不总是数据库本身!


总结:

1)pidstat -t -p1:找出CPU高的THREAD_OS_ID;

2) 可知THREAD_OS_ID查找到SQL文本;