【MySQL 数据宝典】【索引原理】- 006 慢查询日志分析&优化

一、介绍

https://dev.mysql.com/doc/refman/8.0/en/slow-query-log.html

  • MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阈值的语句。
  • 默认情况下,MySQL数据库并不启动慢查询日志,需要手动来设置这个参数。
  • 如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件和数据库表。

二、核心参数

  • 执行下面的语句
sql 复制代码
mysql> show variables like '%slow_query_log%';
+---------------------+------------------------------+
| Variable_name       | Value                        |
+---------------------+------------------------------+
| slow_query_log      | ON                           |
| slow_query_log_file | /var/lib/mysql/test-slow.log |
+---------------------+------------------------------+

mysql> show variables like '%long_query%';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
  • MySQL 慢查询的相关参数解释:
    • slow_query_log :是否开启慢查询日志
      • ON(1)表示开启
      • OFF(0) 表示关闭。
    • slow-query-log-file:新版(5.6及以上版本)MySQL数据库慢查询日志存储路径。
    • long_query_time : 慢查询阈值,当查询时间多于设定的阈值时,记录日志。 单位-秒

三、配置

  1. 默认情况下slow_query_log的值为OFF,表示慢查询日志是禁用的
sql 复制代码
mysql> show variables like '%slow_query_log%';
+---------------------+------------------------------+
| Variable_name       | Value                        |
+---------------------+------------------------------+
| slow_query_log      | ON                           |
| slow_query_log_file | /var/lib/mysql/test-slow.log |
+---------------------+------------------------------+
  1. 可以通过设置slow_query_log的值来开启
sql 复制代码
mysql> set global slow_query_log=1;
  1. 使用set global slow_query_log=1 开启了慢查询日志只对当前数据库生效,MySQL重启后则会失效。如果要永久生效,就必须修改配置文件my.cnf(其它系统变量也是如此)
sql 复制代码
-- 编辑配置
vim /etc/my.cnf

-- 添加如下内容
slow_query_log =1
slow_query_log_file=/var/lib/mysql/test-slow.log

-- 重启MySQL
service mysqld restart

mysql> show variables like '%slow_query%';
+---------------------+--------------------------------+
| Variable_name       | Value                          |
+---------------------+--------------------------------+
| slow_query_log      | ON                             |
| slow_query_log_file | /var/lib/mysql/test-slow.log |
+---------------------+--------------------------------+
  1. 那么开启了慢查询日志后,什么样的SQL才会记录到慢查询日志里面呢? 这个是由参数 long_query_time控制,默认情况下long_query_time的值为10秒.
sql 复制代码
mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

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

mysql>  show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
  1. 修改了变量long_query_time,但是查询变量long_query_time的值还是10,难道没有修改到呢?注意:使用命令 set global long_query_time=1 修改后,需要重新连接或新开一个会话才能看到修改值。
sql 复制代码
mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
  1. log_output 参数是指定日志的存储方式。log_output='FILE' 表示将日志存入文件,默认值是'FILE'。log_output='TABLE' 表示将日志存入数据库,这样日志信息就会被写入到 mysql.slow_log 表中。
sql 复制代码
mysql> SHOW VARIABLES LIKE '%log_output%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | FILE  |
+---------------+-------+

MySQL数据库支持同时两种日志存储方式,配置的时候以逗号隔开即可,如:log_output='FILE,TABLE'。日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,因此对于需要启用慢查询日志,又需要能够获得更高的系统性能,那么建议优先记录到文件.

  1. 系统变量 log-queries-not-using-indexes:未使用索引的查询也被记录到慢查询日志中(可选项)。如果调优的话,建议开启这个选项。
sql 复制代码
mysql> show variables like 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | OFF   |
+-------------------------------+-------+

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

mysql> show variables like 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | ON    |
+-------------------------------+-------+

四、慢查询测试&验证

执行 test_index.sql 脚本,监控慢查询日志内容

shell 复制代码
[root@localhost mysql]# tail -f /var/lib/mysql/test-slow.log 
/usr/sbin/mysqld, Version: 5.7.30-log (MySQL Community Server (GPL)). started with:
Tcp port: 0  Unix socket: /var/lib/mysql/mysql.sock
Time                 Id Command    Argument

执行下面的SQL,执行超时 (超过1秒) 我们去查看慢查询日志

sql 复制代码
SELECT * FROM test_index WHERE  
hobby = '20009951' OR hobby = '10009931' OR hobby = '30009931' 
OR dname = 'name4000' OR dname = 'name6600' ;

日志内容分析

我们得到慢查询日志后,最重要的一步就是去分析这个日志。我们先来看下慢日志里到底记录了哪些内容。

如下图是慢日志里其中一条SQL的记录内容,可以看到有时间戳,用户,查询时长及具体的SQL等信息.

sql 复制代码
# Time: 2022-02-23T13:50:45.005959Z
# User@Host: root[root] @ localhost []  Id:     3
# Query_time: 3.724273  Lock_time: 0.000371 Rows_sent: 5  Rows_examined: 5000000
SET timestamp=1645624245;
select * from test_index where hobby = '20009951' or hobby = '10009931' or hobby = '30009931' or dname = 'name4000' or dname = 'name6600';
  • Time: 执行时间
  • User: 用户信息 ,Id信息
  • Query_time: 查询时长
  • Lock_time: 等待锁的时长
  • Rows_sent:查询结果的行数
  • Rows_examined: 查询扫描的行数
  • SET timestamp: 时间戳
  • SQL的具体信息

五、慢查询 SQL 的优化思路

5.1 核心原因

在日常的运维过程中,经常会遇到DBA将一些执行效率较低的SQL发过来找开发人员分析,当我们拿到这个SQL语句之后,在对这些SQL进行分析之前,需要明确可能导致SQL执行性能下降的原因进行分析,执行性能下降可以体现在以下两个方面:

  • 等待时间长

    锁表导致查询一直处于等待状态,后续我们从MySQL锁的机制去分析SQL执行的原理

  • 执行时间长

    1.查询语句写的烂
    2.索引失效
    3.关联查询太多join
    4.服务器调优及各个参数的设置

5.2 优化思路

优化高并发执行的SQL

优先选择优化高并发执行的SQL,因为高并发的SQL发生问题带来的后果更为严重。比如下面两种情况:

  • SQL1:每小时执行10000次,每次20个IO。优化后每次18个IO,每小时节省2万次IO。
  • SQL2:每小时10次,每次20000个IO。每次优化减少2000个IO,每小时节省2万次IO。

尽管SQL2更难优化,但SQL1属于高并发SQL,更急需优化,且成本更低。

定位优化对象的性能瓶颈

在优化之前,需要了解性能瓶颈所在。在优化SQL时,选择优化的方向有三个:

  1. IO:数据访问消耗的时间过多,查看是否正确使用了索引。
  2. CPU:数据运算花费的时间过多,数据的运算、分组、排序是否有问题。
  3. 网络带宽:是否需要加大网络带宽。

明确优化目标

根据数据库当前状态、与该条SQL的关系以及当前SQL的具体功能,明确优化目标。优化的结果应该能够给用户带来更好的体验,同时需要考虑最好和最差情况下的资源消耗。

EXPLAIN执行计划入手

只有EXPLAIN能够告诉你当前SQL的执行状态,通过执行计划可以更好地理解SQL的执行过程。

永远用小的结果集驱动大的结果集

小的数据集驱动大的数据集,可以减少内层表读取的次数,从而提高性能。

例如,嵌套循环中,如果小的循环在外层,则数据库连接只会发生5次,进行5000次操作,相比之下,如果大的循环在外层,则会导致1000次数据库连接,增加资源消耗。

尽可能在索引中完成排序

排序操作较为常见,如果排序字段在索引中,速度会更快,因为索引本身就是排好序的。否则,需要从表中获取数据,在内存中进行排序,可能会涉及到磁盘IO操作。

只获取自己需要的列

避免使用SELECT *,因为SELECT *很可能不会使用索引,并且会增加数据传输的开销。

只使用最有效的过滤条件

避免使用不必要的过滤条件,应该选择最短的路径访问数据,以提高查询效率。

尽可能避免复杂的JOIN和子查询

复杂的JOIN操作会增加资源消耗,建议每条SQL的JOIN操作不要超过三张表。可以将复杂的SQL拆分成多个小的SQL单独执行,并在程序中进行结果封装。

合理设计并利用索引

如何判定是否需要创建索引?

  • 较为频繁作为查询条件的字段应该创建索引。
  • 唯一性较差的字段不适合创建索引,即使频繁作为查询条件。唯一性较差的字段指的是数据重复度较高的字段,如状态字段、类型字段等。
  • 更新非常频繁的字段不适合创建索引,因为更新索引会增加额外开销。
  • 不会出现在WHERE子句中的字段不应该创建索引。

如何选择合适的索引?

  • 对于单键索引,选择过滤性更好的索引。
  • 对于联合索引,过滤性最好的字段应该排在索引字段顺序的前面。
shell 复制代码
count(distinct column)/count(*)
shell 复制代码
select something from order_table where user_id='1234' order by id limit 23000,100;

改写语句

plsql 复制代码
   select a.something    from order_table a,
     (select id from order_table where user_id='1234' order by id limit 23000,100) b
   where a.id=b.id
   order by a.id;
相关推荐
White_Mountain28 分钟前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
老王笔记33 分钟前
GTID下复制问题和解决
mysql
带电的小王2 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
Lojarro2 小时前
【Spring】Spring框架之-AOP
java·mysql·spring
梦想平凡2 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO2 小时前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
Ewen Seong3 小时前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
元争栈道3 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库3 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道4 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频