20MySQL性能慢排查

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20MySQL性能慢排查相关的知识,希望对你有一定的参考价值。

使用profile工具排查语句

mysql Profile是一种MySQL SQL查询性能优化工具,它可以帮助用户定位和解决MySQL数据库性能问题。此工具可以帮助用户分析查询的性能,并确定哪些查询需要进一步优化。MySQL Profile可以运行在基于Web的管理中心(例如phpMyAdmin或Adminer)或者作为MySQL命令行客户端的插件。通过使用MySQL Profile,用户可以:

1、追踪查询的执行时间和执行流程,以识别缓慢的部分。

2、分析查询执行计划,以确定数据库是如何查询的。

3、识别查询需要优化的部分。

4、生成报告和视觉化分析,以帮助理解查询性能。

5、监视长时间运行或高负载查询,以及其他资源问题,以及确定需要加强或缩减的部分。

#打开后,会显示语句执行详细的过程
set profiling = ON
#查看语句,注意结果中的query_id值
show profiles ;
MySQL的查询分析器可以记录并提供多种查询性能信息,这些信息对于优化查询执行计划非常有用。以下是MySQL查询分析器记录的主要查询性能信息。
Query_ID:每个查询在查询分析器中都有一个唯一的ID。
Duration:查询执行的总时间,包括解析、编译、优化和执行查询本身所需的时间。
Query:执行的查询语句。
Status:查询的执行状态,例如查询成功或失败。
Rows_sent:查询返回的行数。
Rows_examined:查询中检查的行数。
Rows_affected:查询所影响的行数。
tmp_table:MySQL是否需要使用临时表来完成查询,以及临时表的使用时间和大小。
Lock_time:在执行查询时花费的时间等待锁定。
Rows_read:查询操作所使用的磁盘I/O次数。
Rows_sent:查询返回的数据量在网络上传输所花费的时间。

20、MySQL性能慢排查_pidstat

#显示语句的详细执行步骤和时长
Show profile for query #


20、MySQL性能慢排查_profile_02

20、MySQL性能慢排查_mysql

#显示cpu使用情况
Show profile cpu for query #
Query_ID:查询的ID。
Duration:查询总时间(单位:秒)。
Query:查询语句。
User_time:查询执行期间花费的用户模式CPU时间(单位:秒)。
System_time:查询执行期间花费的系统模式CPU时间(单位:秒)。
Total_cpu_time:查询执行期间花费的总CPU时间(单位:秒)。
Profile_timestamp:查询性能信息的时间戳

20、MySQL性能慢排查_mysql

SHOW PROFILE ALL FOR QUERY query_id:显示命令详细操作过程中的所有性能分析器信息,包括读取和获取数据等。

20、MySQL性能慢排查_profile_05

SHOW PROFILE BLOCK IO FOR QUERY query_id:显示块IO操作的性能分析器信息,例如磁盘读取和写入。

20、MySQL性能慢排查_mysql

SHOW PROFILE MEMORY FOR QUERY query_id:显示查询的内存使用情况,例如内存分配、释放和重用。

20、MySQL性能慢排查_profile_07

mysql cpu使用率过高排查

1、使用show full processlist 列出所有线程查看慢查询

2、.查询是否有锁问题

show OPEN TABLES where In_use > 0;

查看正在锁的事务

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

查看等待锁的事务

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

3、开启慢查询日志

slow_query_log=ON

Mysql 内存使用率过高排查

如果您的MySQL内存使用率高,请按照以下步骤进行排查:

1、确认MySQL优化器参数是否正确 - 查看MySQL配置文件中的my.cnf,查看以下参数设置是否正确:

innodb_buffer_pool_size

query_cache_size

table_open_cache

这些参数应该适当配置,符合您的服务器的硬件规格和数据库负载。

2、检查MySQL进程使用的内存 - 在Linux上,可以通过以下命令查看进程使用的内存:

ps aux | grep mysql

您可以计算RES列的总和,该列显示进程使用的物理内存。

3、检查是否有其他应用程序占用了过多的内存 - 您可能会发现其他应用程序使用了很多内存,从而导致MySQL无法获得足够的内存。您可以通过以下命令检查:

top

此命令将显示服务器上所有进程的性能指标。

4、检查是否有长时间运行的查询 - 长时间运行的查询可能占用了太多的MySQL内存,导致内存使用率过高。您可以通过执行以下查询来查找长时间运行的查询:

SELECT * FROM information_schema.processlist WHERE command != Sleep ORDER BY time DESC;

此查询将显示正在运行的所有MySQL进程。

5、检查是否需要优化查询 - 如果您的数据库表很大,或者您的应用程序经常执行大量查询,则可能需要优化查询以提高性能和减少内存使用。您可以使用MySQL Profiler等工具来分析查询并进行优化。

pidstat工具使用

pidstat是一个Linux系统性能分析工具,用于监视进程的CPU使用,内存使用和IO等指标。以下是pidstat的使用示例:

pidstat -u:显示CPU使用情况,包括处理器时间占用率、用户CPU 占用率和内核CPU占用率等信息

pidstat -r:显示内存使用情况,包括进程常驻集、共享内存、虚拟内存和物理内存等指标

pidstat -d:显示磁盘 IO使用情况,包括磁盘读写速率、IOPS和服务时间等信息

pidstat -t:显示每个线程的CPU和内存使用情况

pidstat -p [pid]:指定要监视的进程ID

20、MySQL性能慢排查_profile_08

20、MySQL性能慢排查_mysql

性能分析 | MySQL 的慢查分析实例

技术图片

最近遇见一个 MySQL 的慢查问题,于是排查了下,这里把相关的过程做个总结。

定位原因

我首先查看了 MySQL 的慢查询日志,发现有这样一条 query 耗时非常长(大概在 1 秒多),而且扫描的行数很大(10 多万条数据,差不多是全表了):

SELECT * FROM tgdemand_demand t1
WHERE
(
t1.id IN
(
SELECT t2.demand_id
FROM tgdemand_job t2
WHERE (t2.state = ‘working‘ AND t2.wangwang = ‘abc‘)
)
AND
NOT (t1.state = ‘needConfirm‘)
)
ORDER BY t1.create_date DESC

这个查询不是很复杂,首先执行一个子查询,取到任务的状态(state)是 ‘working’ 并且任务的关联人 (wangwang)是’abc’的所有需求 id(这个设计师进行中的任务对应的需求 id),然后再到主表 tgdemand_demand中带入刚才的 id 集合,查询出需求状态(state)不是 ‘needConfirm’ 的所有需求,最后进行一个排序。

按道理子查询筛选出 id 后到主表过滤是直接使用到主键,应该是很快的啊。而且,我检查了子查询的 tgdemand_job 表的索引,where 中用到的查询条件都已经增加了索引。怎么会这样呢?

于是,我对这个 query 执行了一个 explain(输出 sql 语句的执行计划),看看 MySQL 的执行计划是怎样的。输出如下:

技术图片

我们看到,第一行是 t1 表,type 是 ALL(全表扫描),rows(影响行数)是157089,没有用到任何索引;第二行是 t2 表,用到了索引。和我之前理解的执行顺序完全不一样!

为什么 MySQL 不是先执行子查询,而是对 t1 表进行了全表扫描呢?我们仔细看第二行的 select_type,发现它的值是 DEPENDENT_SUBQUERY,意思是这个子查询的查询方式依赖外层的查询。这是什么意思?

实际上,MySQL 对于这种子查询会进行改写,上面的 SQL 会被改写成下面的形式:

SELECT * FROM tgdemand_demand t1 WHERE EXISTS (
SELECT * FROM tgdemand_job t2 WHERE t1.id = t2.demand_id AND (t2.state = ‘working‘ AND t2.wangwang = ‘abc‘)
) AND NOT (t1.state = ‘needConfirm‘)
ORDER BY t1.create_date DESC;

这表示,SQL 会去扫描 tgdemand_demand 表的所有数据,每条数据再传入到子查询中与表 tgdemand_job 进行关联,执行子查询,子查询根本不会先执行,而且子查询会执行 157089 次(外层表的记录数量)。还好我们的子查询加了必要的索引,不然结果会更加惨不忍睹。

这个结果真是太坑爹,而且十分违反直觉。对于慢查询,千万不要想当然,还是多多 explain,看看数据库实际上是怎么去执行的。

问题修复

既然子查询会被改写,那最简单的解决方案就是不用子查询,将内层获取需求 id 的 SQL 单独拿出来执行,取到结果后再执行一条 SQL 去获取实际的数据。大概像这样(下面的语句是不合法的,只是示意):

ids = SELECT t2.demand_id
FROM tgdemand_job t2
WHERE (t2.state = ‘working‘ AND t2.wangwang = ‘abc‘);

SELECT * FROM tgdemand_demand t1
WHERE
(
t1.id IN ids
AND
NOT (t1.state = ‘needConfirm‘)
)
ORDER BY t1.create_date DESC;

说干咱就干,我找到了下面的代码(是 python 语言写的):

demand_ids = Job.objects.filter(wangwang=user[‘wangwang‘], state=‘working‘).values_list("demand_id", flat=True)

demands = Demand.objects.filter(id__in=demand_ids).exclude(state__in=[‘needConfirm‘]).order_by(‘-create_date‘)

咦!这不是和我想得是一样的嘛?先查出需求 id(代码第一行),然后用 id 集合再去执行实际的查询(代码第二行)。为什么经过 ORM 框架的处理后产出的 SQL 就不一样了呢?

带着这个问题我搜索了一番。原来 Django 自带的 ORM 框架生成的 QuerySet 是懒执行的(lazy evaluated),我们可以将这种 QuerySet 到处传,直到需要时才会实际的执行 SQL。

比如,我们代码里面的 Job.objects.filter(wangwang=user[‘wangwang‘], state=‘working‘).values_list("demand_id", flat=True)这个 QuerySet 实际上并没有执行,就被作为参数传递给了id__in,当Demand.objects.filter(id__in=demand_ids).exclude(state__in=[‘needConfirm‘]).order_by(‘-create_date‘)这个 QuerySet 执行时,刚才未执行的 QuerySet 才开始作为 SQL 执行,于是生成了最开始的 SQL 语句。

既然如此,我们的目的要让 QuerySet 提前执行,获得结果集。根据文档,对 QuerySet 进行循环、slice、取 len、list 转换的时候被执行。于是我将代码更改为了下面的样子:

demand_ids = list(Job.objects.filter(wangwang=user[‘wangwang‘], state=‘working‘).values_list("demand_id", flat=True))

demands = Demand.objects.filter(id__in=demand_ids).exclude(state__in=[‘needConfirm‘]).order_by(‘-create_date‘)

终于,页面打开速度恢复正常了。

实际上,我们也可以对 SQL 进行改写来解决问题:

select * from tgdemand_demand t1, (select t.demand_id from tgdemand_job t where t.state = ‘working‘ and t.wangwang = ‘abc‘) t2
where t1.id=t2.demand_id and not (t1.state = ‘needConfirm‘)
order by t1.create_date DESC

思路是去掉子查询,换用 2 个表进行 join 的方式来取得数据。这里就不展开了。

感想

框架可以提高生产率的前提是对背后的原理足够了解,不然应用很可能就会在某个时间暴露出一些隐蔽的要命问题(这些问题在小规模阶段可能根本都发现不了……)。保证应用的健壮真是个大学问,还有很多东西值得我们去探索。

以上是关于20MySQL性能慢排查的主要内容,如果未能解决你的问题,请参考以下文章

mysql为什么会慢

mysql问题排查与性能优化

Nginx 日志分析及性能排查

Nginx 日志分析及性能排查

Nginx 日志分析及性能排查

MySQL的insert执行操作慢,该怎么整?