如何定位Mysql慢查询和短而频的查询
关于mysql的Long_query_time参数
long_query_time 是 MySQL 中一个非常关键的性能调优参数,它用于定义“慢查询”的阈值。默认值: 10 (秒),执行时间大于这个值的sql,才会被记录为慢sql,可以通过如下来查询这个值
SHOW VARIABLES LIKE 'long_query_time';
可以在my.cnf/my.ini
文件中设置这个值,设置之后要重启
[mysqld]
long_query_time = 2
怎么查询慢日志
方案一:使用 pt-query-digest 工具
上面提到,执行时间大于long_query_time的sql,才会被记录为慢sql。那么怎么查询慢sql呢?
通常,在my.cnf/my.ini
文件中设置好慢日志文件的路径
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 10
log_output = FILE
有慢sql了就会形成/var/log/mysql/mysql-slow.log
文件,然后分析这个文件就需要使用到一个工具pt-query-digest
使用 pt-query-digest 工具分析 MySQL 慢日志
方案二:Mysql8支持直接查表来查询慢日志
把log_output 设置为TABLE 即可
[mysqld]
slow_query_log = 1
long_query_time = 10
log_output = TABLE
怎么查询
SELECT start_time, query_time, sql_text
FROM mysql.slow_log
ORDER BY start_time DESC
LIMIT 5;
需要注意的是,mysql.slow_log 是一张永久写、永不清除 的表,必须建立定期清理机制。
-- 方式 1:直接清空
TRUNCATE TABLE mysql.slow_log;-- 方式 2:只保留最近 7 天(MySQL 8.0 支持)
CALL sys.ps_truncate_slow_log(7);
方案三:同时记录
log_output = FILE,TABLE
SHOW VARIABLES LIKE 'log_output';
-- 结果应显示 FILE,TABLE
怎么分析“短而频”的sql?
比如我们忽然发现CPU被打满了,但是看看又没有慢sql执行,那此时很有可能是有一些不是很慢,但是执行频繁非常高的sql在执行,如果使用上面的方案,永远都无法排查出来
一、先确认开启Performance Schema (默认 ON为开)
SHOW VARIABLES LIKE 'performance_schema';
二、查总耗时排行榜
SELECT DIGEST,DIGEST_TEXT,COUNT_STAR AS calls,SUM_TIMER_WAIT / 1e12 AS total_sec, -- 期间累计耗时AVG_TIMER_WAIT / 1e12 AS avg_sec, -- 单次平均SUM_LOCK_TIME / 1e12 AS lock_sec,SUM_ROWS_SENT AS rows_sent,FIRST_SEEN,LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 5;
total_sec 最大的那条就是最耗CPU的,而不用管他单次执行多少时间。
问题
events_statements_summary_by_digest
记录默认从 MySQL 启动(或上次 TRUNCATE)开始累加,只要实例不重启、不手动清空,数据就一直叠加。老业务 SQL 长期霸榜,看不出“最近 1 小时”突然暴涨的元凶;
所以建议排查时先把历史数据清一下
TRUNCATE events_statements_summary_by_digest;
看下到底有多频,把 calls 除以时间窗口即可得 实际 QPS:
-- 例如 2 分钟前手动 TRUNCATE 过,窗口就是 120 s
SELECT COUNT_STAR / 120 AS qps
FROM events_statements_summary_by_digest
WHERE DIGEST = 'xxx';
三、怎么拿到完整的sql
SELECT SQL_TEXT, TIMER_WAIT/1e12 AS sec
FROM events_statements_history_long
WHERE DIGEST = 'xxx'
ORDER BY TIMER_START DESC
LIMIT 1;
但是events_statements_history_long
表默认也只能存 1024 字节,超过长度就被 末尾截断。在mysql8中,可以设置如下参数来设置存的字节数
[mysqld]
performance_schema_max_sql_text_length = 40960
查询
SELECT @@max_digest_length; -- 指纹长度(默认 1024)
SELECT @@performance_schema_max_sql_text_length; -- 8.0 专用
SHOW VARIABLES LIKE 'performance_schema_max_sql_text_length';