文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

慢SQL探秘之为什么我的SQL很慢却没记录在慢查询日志里

2024-11-30 00:41

关注

在MySQL数据库中,想了解数据库运行情况的重要指标之一是慢SQL。而并非如某些人所说的所有运行慢的SQL都会被记录在慢SQL日志(或日志表)里,抑或是没有慢SQL就代表没有运行慢的SQL。本文将总结一些比较常见的运行比较慢但不会被记录在慢SQL日志里的情况。另外,慢SQL的计算方式在MySQL8.0新版本中有变化,因此,将通过对比MySQL5.7(MySQL5.7.38)与MySQL8.0(MySQL8.0.33)进行总结。

1.  准备工作

部署了两套环境,分别是MySQL5.7(MySQL5.7.38)版本及MySQL8.0(MySQL8.0.33)版本。另外为了后续进行慢SQL测试,此时先创建一张测试表并清空慢SQL日志表。

(1)创建测试表及数据

创建测试表及测试数据,便于后续测试。本次通过创建一张1000W记录的表进行测试。

然后再添加个字段。

mysql> call sp_createNum(10000000);
Query OK, 1611392 rows affected (38.70 sec)
mysql> select  count(*) from  testdb.nums;
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row in set (3.70 sec)
mysql> alter table testdb.nums add c1 varchar(20);
Query OK, 0 rows affected (17.83 sec)
Records: 0  Duplicates: 0  Warnings: 0

(2)清空慢SQL日志表

测试前先清空慢SQL日志表mysql.slow_log,清空方法如下:

mysql> select  count(*) from mysql.slow_log;
+----------+
| count(*) |
+----------+
|        2 |
+----------+
1 row in set (0.00 sec)
# 需先关闭慢SQL监控开关
mysql> set global slow_query_log=0;
Query OK, 0 rows affected (0.00 sec)
# truncate 方式清空慢SQL日志表
mysql> truncate table  mysql.slow_log;
Query OK, 0 rows affected (0.00 sec)
mysql> select  count(*) from mysql.slow_log;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
# 清理完毕后开启慢SQL监控
mysql> set global slow_query_log=1;
Query OK, 0 rows affected (0.00 sec)

2.  未开启慢SQL监控

查看MySQL是否开启MySQL的方法如下:

mysql> SHOW GLOBAL VARIABLES LIKE 'slow_query_log';
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | ON    |
+----------------+-------+
1 row in set (0.00 sec)

其中value值为ON (或1),则代表开启了慢SQL监控。MySQL各个版本查看的方法均一样。

另外和慢SQL相关的其他主要参数如下:

3.  SQL运行时间小于慢SQL监控阈值时间

第一部分已经介绍了和慢SQL相关的参数中的long_query_time,即慢SQL阈值。所以,当SQL运行时间小于该阈值时,对于的SQL将不会记录在慢SQL日志中。查看和修改慢SQL监控阈值的方法如下:

# 查看慢SQL阈值
mysql> SHOW GLOBAL VARIABLES LIKE 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 0.500000 |
+-----------------+----------+
1 row in set (0.00 sec)
# 设置慢SQL阈值
mysql> set global long_query_time=0.6;
Query OK, 0 rows affected (0.00 sec)
# 设置完成后可以查看全局的阈值
mysql> SHOW GLOBAL VARIABLES LIKE 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 0.600000 |
+-----------------+----------+
1 row in set (0.01 sec)
# 但是当前会话的慢SQL阈值是没变的,这个同其他包含全局和会话级的参数类似
mysql> SHOW  VARIABLES LIKE 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 0.500000 |
+-----------------+----------+
1 row in set (0.00 sec)

注:对于不同的数据库需按照实际情况设置慢SQL监控的阈值,例如TP业务的实例且配置相对较好时,建议阈值设置的较低;如果是AP类型业务,则适当放宽慢SQL的阈值。

4. 锁等待或事务等待的SQL

开启2个事务,然后模拟锁等待情况。

(1)MySQL5.7 中测试

首先测试MySQL5.7版本的情况:

事务1

事务2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)


mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 20:40:47 |

+---------------------+

1 row in set (0.00 sec)


mysql> update testdb.nums set c1=id where id<=5;

Query OK, 5 rows affected (7.85 sec)

Rows matched: 5  Changed: 5  Warnings: 0


mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 20:41:07 |

+---------------------+

1 row in set (0.00 sec)



mysql> begin;

Query OK, 0 rows affected (0.00 sec)


mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 20:41:20 |

+---------------------+

1 row in set (0.00 sec)


mysql> update testdb.nums set c1=id where id<3;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 20:42:55 |

+---------------------+

1 row in set (0.00 sec)

mysql> select  * from mysql.slow_log\G

*************************** 1. row ***************************

    start_time: 2024-03-24 20:41:03.204598

     user_host: root[root] @ localhost []

    query_time: 00:00:07.853949

     lock_time: 00:00:00.000089

     rows_sent: 0

 rows_examined: 10000000

            db: testdb

last_insert_id: 0

     insert_id: 0

     server_id: 1

      sql_text: update testdb.nums set c1=id where id<=5

     thread_id: 2

1 row in set (0.00 sec)

mysql> select  * from mysql.slow_log\G

*************************** 1. row ***************************

    start_time: 2024-03-24 20:41:03.204598

     user_host: root[root] @ localhost []

    query_time: 00:00:07.853949

     lock_time: 00:00:00.000089

     rows_sent: 0

 rows_examined: 10000000

            db: testdb

last_insert_id: 0

     insert_id: 0

     server_id: 1

      sql_text: update testdb.nums set c1=id where id<=5

     thread_id: 2

1 row in set (0.00 sec)

从测试情况来看,MySQL5.7的锁等待超时的SQL是没有被记录在慢SQL日志中的

(2)MySQL8.0中测试

事务1

事务2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)


mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 20:59:20 |

+---------------------+

1 row in set (0.00 sec)


mysql> update testdb.nums set c1=id where id<=5;

Query OK, 5 rows affected (12.67 sec)

Rows matched: 5  Changed: 5  Warnings: 0



mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 21:00:01 |

+---------------------+

1 row in set (0.00 sec)



mysql> select  *,CONVERT(sql_text USING utf8mb4)sql_text2  from mysql.slow_log\G

*************************** 1. row ***************************

    start_time: 2024-03-24 20:59:55.819649

     user_host: root[root] @ localhost []

    query_time: 00:00:12.676771

     lock_time: 00:00:00.000003

     rows_sent: 0

 rows_examined: 10000000

            db: testdb

last_insert_id: 0

     insert_id: 0

     server_id: 1

      sql_text: 0x757064617465207465737464622E6E756D73207365742063313D69642077686572652069643C3D35

     thread_id: 87

     sql_text2: update testdb.nums set c1=id where id<=5



mysql> begin;

Query OK, 0 rows affected (0.00 sec)


mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 21:02:21 |

+---------------------+

1 row in set (0.00 sec)


mysql> update testdb.nums set c1=id where id<3;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction


mysql> select now();

+---------------------+

| now()               |

+---------------------+

| 2024-03-24 21:03:41 |

+---------------------+

1 row in set (0.00 sec)


mysql>  select  *,CONVERT(sql_text USING utf8mb4)sql_text2  from mysql.slow_log\G

*************************** 1. row ***************************

    start_time: 2024-03-24 20:59:55.819649

     user_host: root[root] @ localhost []

    query_time: 00:00:12.676771

     lock_time: 00:00:00.000003

     rows_sent: 0

 rows_examined: 10000000

            db: testdb

last_insert_id: 0

     insert_id: 0

     server_id: 1

      sql_text: 0x757064617465207465737464622E6E756D73207365742063313D69642077686572652069643C3D35

     thread_id: 87

     sql_text2: update testdb.nums set c1=id where id<=5

*************************** 2. row ***************************

    start_time: 2024-03-24 21:03:31.882874

     user_host: root[root] @ localhost []

    query_time: 00:01:00.006259

     lock_time: 00:01:00.005760

     rows_sent: 0

 rows_examined: 1

            db: testdb

last_insert_id: 0

     insert_id: 0

     server_id: 1

      sql_text: 0x757064617465207465737464622E6E756D73207365742063313D69642077686572652069643C33

     thread_id: 88

     sql_text2: update testdb.nums set c1=id where id<3

2 rows in set (0.00 sec)

MySQL8.0中,锁等待超时的SQL也会被记录在慢SQL记录中了,这与MySQL8.0后续新版中慢SQL的计算方式有调整有关系。

5. 管理类SQL

管理类SQL指的是alter table、alter user等,默认情况下,此类操作虽然比较慢,超过了慢SQL日志监控的阈值,但是也不会记录在慢SQL日志中。不过可以调整参数log_slow_admin_statements来控制是否记录此类SQL。

(1)默认情况

mysql> use testdb;
Database changed
mysql> alter table testdb.nums add primary key (id);
Query OK, 0 rows affected (1 min 10.93 sec)
Records: 0  Duplicates: 0  Warnings: 0


mysql> select * from mysql.slow_log
    -> \G
Empty set (0.00 sec)

此时,虽然加主键的SQL运行了1分钟以上,但是慢SQL日志表里无此记录。

MySQL8.0 中同样如此。


(2)调整log_slow_admin_statements

log_slow_admin_statements参数是控制记录超时的管理操作SQL是否记录到慢查询日志。默认情况下的值是0,也就是不记录;而将值改为1时,此类SQL将会被记录。

mysql> set global  log_slow_admin_statements=1;
Query OK, 0 rows affected (0.00 sec)


mysql> alter table testdb.nums add key idx_c1(c1);
Query OK, 0 rows affected (16.54 sec)
Records: 0  Duplicates: 0  Warnings: 0


mysql> select * from mysql.slow_log\G
*************************** 1. row ***************************
    start_time: 2024-03-24 21:39:43.181950
     user_host: root[root] @ localhost []
    query_time: 00:00:16.545439
     lock_time: 00:00:00.001927
     rows_sent: 0
 rows_examined: 0
            db: testdb
last_insert_id: 0
     insert_id: 0
     server_id: 1
      sql_text: alter table testdb.nums add key idx_c1(c1)
     thread_id: 8
1 row in set (0.00 sec)


mysql> select version();
+---------------+
| version()     |
+---------------+
| 5.7.38-41-log |
+---------------+
1 row in set (0.00 sec)

此时,添加索引的操作将被记录。

MySQL8.0中同样适用。


6.  扫描记录少于阈值的SQL

MySQL中扫描记录少于阈值由min_examined_row_limit参数控制,默认值为0,即如果SQL扫描的行数少于此值时,将不会被记录在慢SQL日志中,否则将会被记录。由于默认值是0,因此扫描行数>=0的且符合其他记录慢SQL的条件时便会被记录。如果想忽略扫描数据量较少,但是又不想记录超过阈值的SQL,则可以调整min_examined_row_limit来解决。

(1)默认情况

测试一下默认情况.

mysql> show global variables like 'min_examined_row_limit';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| min_examined_row_limit | 0     |
+------------------------+-------+
1 row in set (0.00 sec)
#c1<=999,可以隐式转换导致无法走索引,使其变慢,便于测试
mysql> select count(*) from testdb.nums where c1<=999;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (1.70 sec)
mysql> select * from mysql.slow_log\G
*************************** 1. row ***************************
    start_time: 2024-03-24 21:48:46.005622
     user_host: root[root] @ localhost []
    query_time: 00:00:01.691788
     lock_time: 00:00:00.000092
     rows_sent: 1
 rows_examined: 10000000
            db: testdb
last_insert_id: 0
     insert_id: 0
     server_id: 25455
      sql_text: select count(*) from testdb.nums where c1<=999
     thread_id: 8

此时慢SQL会被记录。MySQL8.0中同样如此。

(2)修改参数

为了测试,此时将min_examined_row_limit值设置为20000000,然后测试是否还会被记录。

mysql> set min_examined_row_limit=20000000;
Query OK, 0 rows affected (0.00 sec)
mysql> select count(*) from testdb.nums where c1<=999;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (1.70 sec)
mysql> select * from mysql.slow_log\G
*************************** 1. row ***************************
    start_time: 2024-03-24 21:48:46.005622
     user_host: root[root] @ localhost []
    query_time: 00:00:01.691788
     lock_time: 00:00:00.000092
     rows_sent: 1
 rows_examined: 10000000
            db: testdb
last_insert_id: 0
     insert_id: 0
     server_id: 25455
      sql_text: select count(*) from testdb.nums where c1<=999
     thread_id: 8
1 row in set (0.00 sec)

可见,此时的慢SQL还是之前的,即修改后,即使SQL运行时间超过了慢SQL阈值,但是扫描行数低于min_examined_row_limit参数指定的值,此时也不会被记录。MySQL同样如此。

7. 其他SQL

除了以上的情况外,复制线程的查询、被DBAkill的正在运行的SQL或部分未运行完毕的SQL也不会记录在慢SQL日志中(不过部分情况再MySQL8.0中有所变更),因此需要大家根据实际情况多总结及测试。

来源:数据库干货铺内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯