【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;
相关推荐
GitLqr7 小时前
Flutter 3.44 插件内置 Kotlin (KGP) 双向兼容适配指南
android·flutter·dart
南墙上的石头8 小时前
麒麟 V10 重装人大金仓 V8R6 踩坑实录(含 MySQL 兼容模式)
数据库·mysql
执子手 吹散苍茫茫烟波10 小时前
RC 隔离级别下 MySQL InnoDB 死锁典型案例
数据库·mysql
随遇丿而安11 小时前
第11周:Activity 跳转与传值 + 跳转优化
android
私人珍藏库12 小时前
[Android] BBLL 开源第三方B哩电视TV端
android·app·生活·工具·多功能
峥无13 小时前
深入理解MySQL事务与MVCC机制
数据库·mysql
杉氧14 小时前
跨平台资源管理:一套代码如何搞定 Android、iOS 和 Web 的图片与多语言?
android·架构·android jetpack
安卓修改大师15 小时前
安卓修改大师实战:从反编译到定制的完整APK修改指南
android
要开心吖ZSH16 小时前
MVCC 进阶:快照读 vs 当前读、幻读与 Next-Key Lock
java·数据库·sql·mysql·mvcc
柚鸥ASO优化16 小时前
安卓APP推广的“降本增效”密码:守好商店内,打好商店外
android·aso优化