SQL语句当前及历史信息查询-performance schema的使用

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文介绍了如何使用MySQL的Performance Schema来获取SQL语句的当前和历史执行信息。Performance Schema默认在MySQL 8.0中启用,可以通过查询相关表来获取详细的SQL执行信息,包括当前执行的SQL、历史执行记录和统计汇总信息,从而快速定位和解决性能瓶颈。

1 缘起

想起这个题目的起因来自于近期的一次性能优化。要优化的是一个网上在线考试系统,这个系统是用spring框架开发的,部署也是常见的方式。后端是mysql数据库,应用服务器是tomcat,考试人员从互联网上登录进行考试。

这个考试系统上要进行一次规模较大的在线考试,大概有800多人要同在一个考场上进行考试。以前这个考试系统上一次考试最大的人数是200人左右。由于同时考试的人数增长了3倍。因此,开发人员做了一个接口用来测试在800人同时在线时应用的响应,结果在300个并发时就出了问题,测试人员提交了答案后系统在数秒后才能响应,最长的响应时间甚至达到了10秒之上。这个响应时间显然满足不了在线考试的要求,需要对系统进行优化。

2 系统优化

优化的过程不是这篇文章的重点,这里就不详细记述了,这里说一下优化采取的措施和效果。

这次优化采取了两个措施。一个措施是针对数据库的,根据更新语句的两个条件创建联合索引。这个措施取得了立竿见影的效果,系统响应时间马上就降到了2-3秒左右。两三秒的时间对于在线考试来说,时间还是长点,查看数据库,偶尔还能发现比较长的事务。下一个优化的措施是更换了spring应用的日志系统的版本,日志系统版本换了之后,再进行压测,响应时间就在1秒之内了,后来分析问题根源是应用服务器的io较慢。

这个系统的全链路涉及互联网、tomcat服务器、spring应用、MySQL数据库,再加上整个系统是部署在windows操作系统之上。诊断和排查起来比较困难。很长一段时间内,MySQL数据都是重点排查的对象,认为是这个考试系统执行的更新语句执行时间导致了整个系统响应时间过长,直至应用运维对方法的执行时间进行监控,确认方法的执行时都在合理范围之后才转到其他的方向。

从数据库方面怎样确认某个时间段内SQL语句的执行时间有无异常?对Oracle数据库,这不是问题,不但有大量的视图、日志可以采集分析,也有ash、AWR等工具可以快速分析和定位。对于MySQL数据库,在performance schema出现之前,技术手段是相对缺乏的,常用的如慢日志、查看进程列表等,获得的信息相对有限,排查起来难度较大。

不过,从performance schema 出现之后,这种情况的得到了改善,尤其是MySQL 8.0以来,performance schema默认是打开的(很多MySQL数据库的大部分参数都是默认配置),这个dba分析数据库性能提供了很大方便。

3 如何使用performance schema获取SQL语句执行的当前和历史信息

3.1 确认performance schema已打开

从MySQL 8.0开始,performance schema默认是打开的,运行下面命令检查performance schema是否打开。

show variables like 'performance_schema';
Variable_name     |Value|
------------------+-----+
performance_schema|ON   |

3.2 确认statement instrument已开启

MySQL8.0默认激活了statement事件的instrument,使用下面的语句确认一下:

select * from setup_instruments where name like '%statement%';
NAME                                         |ENABLED|TIMED|
---------------------------------------------+-------+-----+
statement/sql/select                         |YES    |YES  |
statement/sql/create_table                   |YES    |YES  |
statement/sql/create_index                   |YES    |YES  |
statement/sql/alter_table                    |YES    |YES  |
statement/sql/update                         |YES    |YES  |
statement/sql/insert                         |YES    |YES  |
statement/sql/insert_select                  |YES    |YES  |
statement/sql/delete                         |YES    |YES  |
statement/sql/truncate                       |YES    |YES  |
statement/sql/drop_table                     |YES    |YES  |
statement/sql/drop_index                     |YES    |YES  |

可以看到,常见SQL语句类型的instrument都已激活,如果里面没有想要度量的语句类型,对这个表进行更新即可,例如:

update setup_instruments set ENABLED='NO' where name like 'statement/sql/select'--取消select语句的度量
update setup_instruments set ENABLED='YES' where name like 'statement/sql/select'--激活select语句的度量

3.3 确认已开启的statement 消费者

select * from setup_consumers; NAME                            |ENABLED|--------------------------------+-------+events_stages_current           |NO     | events_stages_history           |NO     | events_stages_history_long      |NO     | events_statements_current       |YES    | events_statements_history       |YES    | events_statements_history_long  |NO     | events_transactions_current     |YES    | events_transactions_history     |YES    | events_transactions_history_long|NO     | events_waits_current            |NO     | events_waits_history            |NO     | events_waits_history_long       |NO     | global_instrumentation          |YES    | thread_instrumentation          |YES    | statements_digest               |YES    |

statement事件的current和history的消费者都是激活的,通过查询这个消费者对应的表可以获得sql语句的相关信息,history_long消费者则没有激活,如果要查询相关信息,必须先进行激活,同样,更新这个表即可:

update setup_consumers set ENABLED='YES' where name like 'events_transactions_history_long'

检查并确认过instrument和consumer之后,就可以查询相关的SQL语句的当前和历史执行信息进行查询、统计和分析了。在这之前需要了解一下performance schema表是怎样显示时间的。

3.4 performance schema的时间显示

关于时间显示,先看一个示例:

select THREAD_ID,EVENT_NAME ,TIMER_START,TIMER_END,TIMER_WAIT from events_statements_current;
THREAD_ID|EVENT_NAME              |TIMER_START     |TIMER_END       |TIMER_WAIT|
---------+------------------------+----------------+----------------+----------+
       47|statement/sql/select    |  39387592241000|  39388252268000| 660027000|
       48|statement/sql/select    |3004210248928000|3004214933894000|4684966000|

这里显示的当前执行的SQL语句,有三个时间列,分别事件的起止时间和等待时间,这些时间值都很大,如果不对这个时间的显示方式进行了解,很难理解其中含义。

performance schema的时间的显示单位是皮秒,时间单位之间的转换如下:

时间的单位:1秒(s) = 1000 毫秒(ms) = 1,000,000 微秒(μs) = 1,000,000,000 纳秒(ns) = 1,000,000,000,000 皮秒(ps)

至于为什么选择这个时间单位,则是基于性能方面的考虑,performance schema在对时间进行采用度量时分别采用cpu周期、纳秒、微秒、毫秒等时间单位。在显示是如果转换cycles或者纳秒到微秒,就要对每一个instrumentation执行除法。除法在很多平台上是比较昂贵的操作,所以采用了皮秒,在转换时进行乘法运算。上面的TIMER_START显示的是从数据库启动时间开始计时的皮秒数,如需显示SQL语句开始和结束的正确时间,需要将它加上数据库的启动时间。

为了方便对时间的显示,MySQL从8.0.16版本提供了format_pico_time 函数,可以把时间转换成可读的时间格式。如:

select format_pico_time(10000000000000) ,format_pico_time(50000000000000000000);
format_pico_time(10000000000000)|format_pico_time(50000000000000000000)|
--------------------------------+--------------------------------------+
10.00 s                         |578.70 d                              |

3.5 显示当前和历史SQL语句执行信息

3.5.1 显示当前SQL语句执行信息

events_statements_current表里显示的现在正在执行的语句,对于空连接,则显示最后执行的语句。要注意的是,当执行的是存储程序时,每一个连接可能会有多行。要想对数据库当前执行的SQL语句进行全面的了解和分析,只需要查询这个表里非空连接执行的SQL语句相关信息即可。

select
  THREAD_ID,
  EVENT_ID,
  db_start.db_start + INTERVAL events_statements_current.TIMER_START / 1000000000000 second  TIMER_START ,
  db_start.db_start + INTERVAL events_statements_current.TIMER_END / 1000000000000 second  TIMER_END,
  format_pico_time(TIMER_WAIT) TIMER_WAIT,
  format_pico_time(LOCK_TIME) LOCK_TIME,
  left(SQL_TEXT,
  80) SQL_TEXT
from
  events_statements_current
inner join (
  select
    now()-INTERVAL VARIABLE_VALUE second + INTERVAL 8 hour db_start
  from
    global_status
  where
    variable_name = 'Uptime') db_start on
  1 = 1
where
  CURRENT_SCHEMA = 'sysbenchdb' ;
THREAD_ID|EVENT_ID|TIMER_START               |TIMER_END                 |TIMER_WAIT|LOCK_TIME|SQL_TEXT                               |
---------+--------+--------------------------+--------------------------+----------+---------+---------------------------------------+
       50|     897|2024-10-14 09:24:14.246050|2024-10-14 09:24:14.293313|47.26 ms  |254.00 us|DELETE FROM sbtest3 WHERE id=50157     |
       51|     897|2024-10-14 09:24:14.283466|2024-10-14 09:24:14.293448|9.98 ms   |69.00 us |DELETE FROM sbtest4 WHERE id=49790     |
       52|     929|2024-10-14 09:24:14.220511|2024-10-14 09:24:14.293462|72.95 ms  |63.00 us |UPDATE sbtest1 SET k=k+1 WHERE id=53649|
       53|     929|2024-10-14 09:24:14.291142|2024-10-14 09:24:14.293469|2.33 ms   |345.00 us|UPDATE sbtest3 SET k=k+1 WHERE id=49856|                                           |

上面的查询显示了MySQL数据库当前正在执行的SQL语句(连接处于活跃状态),以及sleep连接的最后一条执行的sql语句,对于当前正在执行的SQL语句,TIMER_END的值是当前的时间。在这里可以看到语句执行的开始时间,语句的执行时间,也就是这个时间的TIMER_WAIT,以及语句等待表锁的时间,提供了MySQL数据当前正在执行的SQL语句全面准确的时间指标,通过这些时间指标,可以迅速判断SQL语句的执行性能,也有助于迅速定位出MySQL数据库当前执行的慢查询语句。

3.5.2 查询和分析SQL语句执行历史信息

events_statements_history表里可以查询每一个连接最后执行的语句。每个连接保持的语句的数量的上限由performance_schema_events_history_size(缺省值是10)。当连接关闭时,连接执行的语句也被移除。

select
  THREAD_ID,
  EVENT_ID,
  db_start.db_start + INTERVAL events_statements_history .TIMER_START / 1000000000000 second  TIMER_START ,
  db_start.db_start + INTERVAL events_statements_history .TIMER_END / 1000000000000 second  TIMER_END,
  format_pico_time(TIMER_WAIT) TIMER_WAIT,
  format_pico_time(LOCK_TIME) LOCK_TIME,
  left(SQL_TEXT,
  80) SQL_TEXT
from
  events_statements_history
inner join (
  select
    now()-INTERVAL VARIABLE_VALUE second + INTERVAL 8 hour db_start
  from
    global_status
  where
    variable_name = 'Uptime') db_start on
  1 = 1
where
  thread_id=50 ;
THREAD_ID|EVENT_ID|TIMER_START               |TIMER_END                 |TIMER_WAIT|LOCK_TIME|SQL_TEXT                                                                        |
---------+--------+--------------------------+--------------------------+----------+---------+--------------------------------------------------------------------------------+
       50|   37741|2024-10-14 09:28:58.021348|2024-10-14 09:28:58.021436|88.54 us  |34.00 us |SELECT c FROM sbtest1 WHERE id=50452                                            |
       50|   37743|2024-10-14 09:28:58.022504|2024-10-14 09:28:58.022656|151.96 us |34.00 us |SELECT c FROM sbtest1 WHERE id=49969                                            |
       50|   37745|2024-10-14 09:28:58.024400|2024-10-14 09:28:58.028273|3.87 ms   |39.00 us |SELECT c FROM sbtest1 WHERE id=50001                                            |
       50|   37747|2024-10-14 09:28:58.029995|2024-10-14 09:28:58.030251|255.92 us |37.00 us |SELECT c FROM sbtest1 WHERE id=50318                                            |
       50|   37749|2024-10-14 09:28:58.031132|2024-10-14 09:28:58.031496|364.34 us |35.00 us |SELECT c FROM sbtest2 WHERE id BETWEEN 63885 AND 63984                          |
       50|   37751|2024-10-14 09:28:58.032481|2024-10-14 09:28:58.032870|389.26 us |38.00 us |SELECT SUM(k) FROM sbtest3 WHERE id BETWEEN 50246 AND 50345                     |
       50|   37753|2024-10-14 09:28:58.034395|2024-10-14 09:28:58.034696|301.60 us |42.00 us |SELECT c FROM sbtest2 WHERE id BETWEEN 50298 AND 50397 ORDER BY c               |
       50|   37755|2024-10-14 09:28:58.040730|2024-10-14 09:28:58.041030|299.84 us |52.00 us |SELECT DISTINCT c FROM sbtest3 WHERE id BETWEEN 50067 AND 50166 ORDER BY c      |
       50|   37757|2024-10-14 09:28:58.042091|2024-10-14 09:28:58.098982|56.89 ms  |214.00 us|UPDATE sbtest2 SET k=k+1 WHERE id=61546                                         |
       50|   37759|2024-10-14 09:28:58.101153|2024-10-14 09:28:58.199963|98.81 ms  |357.00 us|UPDATE sbtest3 SET c=select60937771154-82278555870-40236015161-45154483438-439808

上面查询的是一个特定的线程执行的sql语句历史信息,由于performance_schema_events_history_size采用的默认值,对于每个线程只能显示10条语句执行历史信息,如果数据库里有比较长的事务,可以根据事务的SQL语句数量调整这个参数值。从这个表里,我们可以准确把我线程执行在一段时间内执行的SQL语句。对应到前面提到的考试系统,可以跟踪到考生的答题到提交答案,进入到下一题执行的所有SQL语句,可以找到执行时间长响应语句进行分析优化。

3.6 SQL语句执行统计汇总信息

events_statemens_summary_by_digest这个表实际上是实例自这个表重置以来(通常是mysql重启)所有查询的报告。这个表上有一个 唯一索引(shema_name,DIGEST)用来分组数据。表的最大行数由performance_schema_digests_size(动态大小,通常是缺省值10000)决定。可以对这个表进行truncate操作,相当于对这个表进行重置了。对于取样的查询信息,取样的查询在满足下面三个条件之一的情况下被替换:

1 查询第一遇到一个给定的缺省模式

2 新出现的查询比现在取样的查询由更高的timer_wait值(其实就是它执行的更慢)

3 如果performance_schema_max_digest_sample_age选项大于0,现在的查询比这个值老(这个值的单位是秒)。这个值默认是60秒,在你的监控周期是1在你的监控周期是1分钟时这个值设置的正好。因为它恰好可以捕捉到每一分钟时间间隔内执行最慢的sql语句。如果你的监控间隔大了,考虑以下增大这个值。

select left(DIGEST_TEXT,80) sql_text,count_star,
      format_pico_time(MIN_TIMER_WAIT) MIN_TIMER_WAIT ,
      format_pico_time(AVG_TIMER_WAIT) AVG_TIMER_WAIT,
       format_pico_time(MAX_TIMER_WAIT) MAX_TIMER_WAIT,
       format_pico_time(QUANTILE_95) QUANTILE_95,
       format_pico_time(QUANTILE_99) QUANTILE_99,
       format_pico_time(QUANTILE_999) QUANTILE_999   
  from events_statements_summary_by_digest where schema_name='sysbenchdb';                                                                    sql_text  
                                                                   sql_text    |count_star|MIN_TIMER_WAIT|AVG_TIMER_WAIT|MAX_TIMER_WAIT|QUANTILE_95|QUANTILE_99|QUANTILE_999|
-----------------------------------------------------------------------------+----------+--------------+--------------+--------------+-----------+-----------+------------+
SELECT `c` FROM `sbtest4` WHERE `id` = ?                                     |     11268|60.64 us      |329.87 us     |76.17 ms      |660.69 us  |3.47 ms    |10.96 ms    |
SELECT `c` FROM `sbtest3` WHERE `id` = ?                                     |     10000|59.63 us      |349.34 us     |77.63 ms      |660.69 us  |3.63 ms    |12.02 ms    |
SELECT `c` FROM `sbtest4` WHERE `id` BETWEEN ? AND ?                         |      1055|152.94 us     |523.77 us     |30.97 ms      |870.96 us  |4.79 ms    |19.95 ms    |
SELECT `c` FROM `sbtest3` WHERE `id` BETWEEN ? AND ?                         |      1089|144.69 us     |492.87 us     |22.03 ms      |954.99 us  |4.57 ms    |16.60 ms    |
SELECT SUM ( `k` ) FROM `sbtest1` WHERE `id` BETWEEN ? AND ?                 |      1091|126.12 us     |551.48 us     |45.39 ms      |870.96 us  |5.50 ms    |25.12 ms    |
SELECT SUM ( `k` ) FROM `sbtest3` WHERE `id` BETWEEN ? AND ?                 |      1076|119.57 us     |443.45 us     |15.09 ms      |870.96 us  |4.17 ms    |12.59 ms    |
SELECT `c` FROM `sbtest3` WHERE `id` BETWEEN ? AND ? ORDER BY `c`            |      1017|192.71 us     |594.76 us     |24.29 ms      |1.05 ms    |5.75 ms    |18.20 ms    |
SELECT SUM ( `k` ) FROM `sbtest4` WHERE `id` BETWEEN ? AND ?                 |      1075|133.53 us     |442.39 us     |30.17 ms      |758.58 us  |4.37 ms    |12.02 ms    |
SELECT DISTINCTROW `c` FROM `sbtest1` WHERE `id` BETWEEN ? AND ? ORDER BY `c`|      1094|261.42 us     |714.68 us     |33.61 ms      |1.15 ms    |6.61 ms    |17.38 ms    |
SELECT `c` FROM `sbtest4` WHERE `id` BETWEEN ? AND ? ORDER BY `c`            |      1105|188.01 us     |607.64 us     |56.86 ms      |1.00 ms    |3.80 ms    |22.91 ms    |
SELECT DISTINCTROW `c` FROM `sbtest2` WHERE `id` BETWEEN ? AND ? ORDER BY `c`|      1119|250.87 us     |757.47 us     |47.49 ms      |1.38 ms    |4.57 ms    |34.67 ms    |

这个汇总表也很有用,同样,performance_schema_max_digest_sample_age采用的是默认配置(60秒),这个从这里看一看到一条语句在60秒之内的各项统计指标。对应到上面的考试系统,可以把这个值调整到一场考试的时间。在考试开始前truncate一下这张表。考试完毕后,这个表里就有了整个考试时间内执行的SQL语句的统计信息,从里面可以找到执行时间过长的考试相关的SQL语句,对于性能分析和优化是十分有帮助的,也可以迅速判断是不是数据库是不是整个系统响应慢的原因。

4 总结

总结一下,MySQL的performance schema在分析MySQL数据库SQL语句方面是一个强大的工具,提供了十分详细全面SQL语句当前和历史执行信息,使用起来比慢日志和通用日志更加方便。当然,performance schema的功能不仅局限在SQL语句方面,在事务、线程、等待事件的信息度量和统计分析方面也很强大,是MySQL DBA有必要掌握的性能工具。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
14天前
|
SQL 存储 人工智能
Vanna:开源 AI 检索生成框架,自动生成精确的 SQL 查询
Vanna 是一个开源的 Python RAG(Retrieval-Augmented Generation)框架,能够基于大型语言模型(LLMs)为数据库生成精确的 SQL 查询。Vanna 支持多种 LLMs、向量数据库和 SQL 数据库,提供高准确性查询,同时确保数据库内容安全私密,不外泄。
79 7
Vanna:开源 AI 检索生成框架,自动生成精确的 SQL 查询
|
21天前
|
SQL Java
使用java在未知表字段情况下通过sql查询信息
使用java在未知表字段情况下通过sql查询信息
34 8
|
28天前
|
SQL 安全 PHP
PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全
本文深入探讨了PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全。
47 4
|
1月前
|
SQL 存储 缓存
如何优化SQL查询性能?
【10月更文挑战第28天】如何优化SQL查询性能?
112 10
|
1月前
|
SQL 关系型数据库 MySQL
|
3月前
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
5月前
|
SQL 存储 监控
SQL Server的并行实施如何优化?
【7月更文挑战第23天】SQL Server的并行实施如何优化?
129 13
|
5月前
|
SQL
解锁 SQL Server 2022的时间序列数据功能
【7月更文挑战第14天】要解锁SQL Server 2022的时间序列数据功能,可使用`generate_series`函数生成整数序列,例如:`SELECT value FROM generate_series(1, 10)。此外,`date_bucket`函数能按指定间隔(如周)对日期时间值分组,这些工具结合窗口函数和其他时间日期函数,能高效处理和分析时间序列数据。更多信息请参考官方文档和技术资料。
|
5月前
|
SQL 存储 网络安全
关系数据库SQLserver 安装 SQL Server
【7月更文挑战第26天】
69 6
|
5月前
|
存储 SQL C++
对比 SQL Server中的VARCHAR(max) 与VARCHAR(n) 数据类型
【7月更文挑战7天】SQL Server 中的 VARCHAR(max) vs VARCHAR(n): - VARCHAR(n) 存储最多 n 个字符(1-8000),适合短文本。 - VARCHAR(max) 可存储约 21 亿个字符,适合大量文本。 - VARCHAR(n) 在处理小数据时性能更好,空间固定。 - VARCHAR(max) 对于大文本更合适,但可能影响性能。 - 选择取决于数据长度预期和业务需求。
453 1