MySQL数据库开启、分析慢SQL

MySQL数据库开启、分析慢SQL

背景:业务人员对性能要求很高,总是想让系统快速响应,遇到此类问题分析性能从两个方面入手

  1. 分析代码,是否有死循环、死锁、内存泄露、内存溢出等
  2. 分析SQL语句是否有慢SQL,SQL查询是否走了索引,SQL写的逻辑是否有问题等。

慢查询日志可以用来定位执行时间很长的查询,它是我们常用的性能分析工具,通过在开发、测试期间关注慢查询,我们可以尽量避免引入效率很差的查询,来分析一波。

说干就干-开启慢SQL

慢SQL开启情况查询

mysql> SHOW VARIABLES LIKE '%slow_query_log%';
+---------------------+-----------------------------------+
| Variable_name       | Value                             |
+---------------------+-----------------------------------+
| slow_query_log      | ON                                |
| slow_query_log_file | /var/lib/mysql/localhost-slow.log |
+---------------------+-----------------------------------+
2 rows in set (0.03 sec)

slow_query_log的value为ON时是开启了慢SQL查询
如果配置文件或启动参数没有给出file_name值,慢查询日志将默认命名为“主机名-slow.log”,如果给出了文件名,但不是绝对路径名,文件则写入数据目录。

方法一开启慢SQL

关闭慢sql查询

mysql> SET GLOBAL slow_query_log=0;
Query OK, 0 rows affected (0.01 sec)
mysql>  SHOW VARIABLES LIKE '%slow_query_log%';
+---------------------+-----------------------------------+
| Variable_name       | Value                             |
+---------------------+-----------------------------------+
| slow_query_log      | OFF                               |
| slow_query_log_file | /var/lib/mysql/localhost-slow.log |
+---------------------+-----------------------------------+
2 rows in set (0.00 sec)

重新开启

mysql> SET GLOBAL slow_query_log=1;
Query OK, 0 rows affected (0.01 sec)
mysql>  SHOW VARIABLES LIKE '%slow_query_log%';
+---------------------+-----------------------------------+
| Variable_name       | Value                             |
+---------------------+-----------------------------------+
| slow_query_log      | ON                                |
| slow_query_log_file | /var/lib/mysql/localhost-slow.log |
+---------------------+-----------------------------------+
2 rows in set (0.00 sec)
mysql> 

慢SQL的阈值 默认10秒

mysql> SHOW GLOBAL VARIABLES LIKE '%long_query_time%';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 10.00000 |
+-----------------+----------+
1 row in set (0.00 sec)
mysql> SET GLOBAL long_query_time=1;
Query OK, 0 rows affected (0.00 sec)

方法二修改配置

[root@localhost ~]# vim /etc/my.cnf
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/lib/mysql/localhost-slow.log # MySQL的日志路径,要有权限
long_query_time = 1
[root@localhost ~]# service mysqld restart
Redirecting to /bin/systemctl restart mysqld.service
[root@localhost ~]#

测试慢SQL

mysql> SELECT SLEEP(12),t.* FROM testTable t WHERE test_id =1  ;
+-----------+---------+-----------------+-------------+------------+
| SLEEP(12) | test_id | test_title      | test_author | test_date  |
+-----------+---------+-----------------+-------------+------------+
|         0 |       1 | 这是第一条      | 王德卿      | 2020-11-19 |
+-----------+---------+-----------------+-------------+------------+
1 row in set (12.02 sec)
mysql> 

查看慢SQL

[root@localhost ~]# more /var/lib/mysql/localhost-slow.log
# Time: 2020-11-19T10:09:45.122744Z
# User@Host: root[root] @  [192.168.170.1]  Id:     4
# Query_time: 12.232602  Lock_time: 0.228128 Rows_sent: 1  Rows_examined: 1
use test001;
SET timestamp=1605780585;
SELECT SLEEP(12),t.* FROM testTable t where test_id =1   LIMIT 0, 1000;

慢SQL详解

  1. 优化策略

碰到了慢SQL不要着急,首先找到前10个慢SQL进行优化,优化完可能会有一些提升,随着前几个优化的结束,再新一轮的优化10个策略,随着用户流量的增加,可以进行多轮次优化。

  1. 慢查询日志的格式
  • Query_time:查询耗时。
  • Rows_examined:检查了多少条记录。
  • Rows_sent:返回了多少行记录(结果集)。

这三条信息基本能看出一条SQL的查询成本

/usr/sbin/mysqld, Version: 5.7.31-log (MySQL Community Server (GPL)). started with:
Tcp port: 0  Unix socket: /var/lib/mysql/mysql.sock
Time                 Id Command    Argument
# Time: 2020-11-23T10:15:11.876535+08:00
# User@Host: root[root] @  [192.168.170.1]  Id:     4
# Query_time: 1.381814  Lock_time: 0.004785 Rows_sent: 1  Rows_examined: 4194304
use test001;
SET timestamp=1606097711;
SELECT COUNT(member_id) id  FROM ums_member_statistics_info_slow  LIMIT 0, 1000;
  • Time:客户端查询时间
  • root[root] :客户端查询用户和IP
  • Lock_time:等待table lock的时间,注意InnoDB的行锁等待是不会反应在这里的。

使用工具分析慢查询日志

但在一个高并发的数据库服务上,或者在做压力测试时,如果发现慢查询日志增长得非常快,很难筛选和查找里面的信息,那么在这种情况下,有如下两种选择。

  • 调整阈值,先设置为较大的阈值,这样慢查询记录就很少了,等优化得差不多了,再减少阈值,不断进行优化。
  • 使用命令/脚本、工具进行分析,如mysqldumpslow、pt-query-digest等。

第一种方法比较繁琐,建议大家使用第二种方法。如果优化效果比较理想,希望更进一步调优,则可以减低阈值,然后记录更多的慢查询日志,然后继续使用脚本、工具进行分析。

使用操作系统命令分析

可以使用操作系统自带的命令进行一些简单的统计,如grep、awk、wc,但不容易实现更高级的筛选排序。

  1. 原生态命令行

查询每秒钟查询的次数,如果你的shell编程能力很强,可以做个很全面的分析

#
[root@localhost ~]# awk '/^# Time:/{print $3, $4, c;c=0}/^# User/{c++}' /var/lib/mysql/localhost-slow.log
2020-11-23T01:19:36.996394Z  
2020-11-23T01:19:49.843742Z  1
……
2020-11-23T10:59:43.769142+08:00  1
[root@localhost ~]# 
  1. mysqldumpslow 工具进行分析

mysqldumpslow 使用方法和技巧

[root@localhost ~]# man mysqldumpslow
#查看一下这个命令怎么使用

mysqldumpslow经常使用的参数:
-s,是order的顺序
----- al 平均锁定时间
-----ar 平均返回记录时间
-----at 平均查询时间(默认)
-----c 计数
-----l 锁定时间
-----r 返回记录
-----t 查询时间
-t,是top n的意思,即为返回前面多少条的数据
-g,后边可以写一个正则匹配模式,大小写不敏感的
文本出处

mysqldumpslow查看文件如下

[root@localhost ~]# mysqldumpslow /var/lib/mysql/localhost-slow.log

Reading mysql slow query log from /var/lib/mysql/localhost-slow.log
Count: 9  Time=1.92s (17s)  Lock=0.00s (0s)  Rows=1.0 (9), root[root]@[192.168.170.1]
  SELECT COUNT(*) id FROM ums_member_statistics_info_slow WHERE consume_amount =N  LIMIT N, N

……
Count: 1  Time=1.25s (1s)  Lock=0.00s (0s)  Rows=1.0 (1), root[root]@[192.168.170.1]
  select count(*) id from ums_member_statistics_info_slow where member_id =N  LIMIT N, N
[root@localhost ~]# 

访问时间最长的10个sql语句的命令如下。

[root@localhost ~]# mysqldumpslow -t 10 /var/lib/mysql/localhost-slow.log

Reading mysql slow query log from /var/lib/mysql/localhost-slow.log
Count: 9  Time=1.92s (17s)  Lock=0.00s (0s)  Rows=1.0 (9), root[root]@[192.168.170.1]
  SELECT COUNT(*) id FROM ums_member_statistics_info_slow WHERE consume_amount =N  LIMIT N, N
……
Count: 1  Time=1.25s (1s)  Lock=0.00s (0s)  Rows=1.0 (1), root[root]@[192.168.170.1]
  select count(*) id from ums_member_statistics_info_slow where member_id =N  LIMIT N, N

Died at /usr/bin/mysqldumpslow line 167, <> chunk 13.
[root@localhost ~]# 

访问次数最多的10个sql语句的命令如下。

[root@localhost ~]# mysqldumpslow -s c -t 10 /var/lib/mysql/localhost-slow.log

Reading mysql slow query log from /var/lib/mysql/localhost-slow.log
Count: 9  Time=1.92s (17s)  Lock=0.00s (0s)  Rows=1.0 (9), root[root]@[192.168.170.1]
  SELECT COUNT(*) id FROM ums_member_statistics_info_slow WHERE consume_amount =N  LIMIT N, N
……
Count: 1  Time=1.25s (1s)  Lock=0.00s (0s)  Rows=1.0 (1), root[root]@[192.168.170.1]
  select count(*) id from ums_member_statistics_info_slow where member_id =N  LIMIT N, N

Died at /usr/bin/mysqldumpslow line 167, <> chunk 13.
[root@localhost ~]# 

访问记录集最多的10个sql语句的命令如下。

[root@localhost ~]# mysqldumpslow -s r -t 10 /var/lib/mysql/localhost-slow.log

对查询到的条件进行过滤 -g参数

[root@localhost ~]#  mysqldumpslow -s c -t 10 -g "count\(\*" /var/lib/mysql/localhost-slow.log
还有很多使用方法,根据场景进行编写使用man mysqldumpslow 进行查询。

3.pt-query-digest工具使用

pt-query-digest 比mysqldumpslow更强大、更友好,可以生成一份比mysqldumpslow更好的可读性好的报告。
pt-query-digest也是最应该被掌握的一个工具。它可以分析MySQL的各种日志,如慢查询日志、generel日志,也可以分析SHOW PROCESSLIST的输出。配合tcpdump我们还可以对线上数据库流量进行采样,实时监控数据库流量,及时发现性能问题。
安装如下

[root@localhost ~]# wget www.percona.com/get/pt-query-digest
chmod u+x pt-query-digest--2020-11-23 13:18:20--  http://www.percona.com/get/pt-query-digest
Resolving www.percona.com (www.percona.com)... 74.121.199.235
Connecting to www.percona.com (www.percona.com)|74.121.199.235|:80... 
……
Connecting to www.percona.com (www.percona.com)|74.121.199.235|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 529350 (517K) [text/plain]
Saving to: ‘pt-query-digest’

100%[=======================================================================================================================================================>] 529,350     14.9KB/s   in 33s    

2020-11-23 13:18:56 (15.5 KB/s) - ‘pt-query-digest’ saved [529350/529350]

[root@localhost ~]# chmod u+x pt-query-digest
[root@localhost ~]# ll
-rwxr--r--. 1 root root    529350 Sep  2 22:55 pt-query-digest
[root@localhost ~]# 

牛刀小试

[root@localhost ~]# ./pt-query-digest /var/lib/mysql/localhost-slow.log 
Can't locate Data/Dumper.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at ./pt-query-digest line 75.
BEGIN failed--compilation aborted at ./pt-query-digest line 75.
哎呦我嘞个去,翻车了!

打工人,不抛弃不放弃,go on

[root@localhost ~]# yum install -y perl-CPAN perl-Time-HiRes
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
……
  systemtap-sdt-devel.x86_64 0:4.0-13.el7      

Complete!



[root@localhost ~]# ./pt-query-digest /var/lib/mysql/localhost-slow.log 
Can't locate Digest/MD5.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at ./pt-query-digest line 2492.
BEGIN failed--compilation aborted at ./pt-query-digest line 2492.
继续翻车

抛弃了就不是打工人,加个油继续前进

[root@localhost ~]# yum -y install perl-Digest-MD5
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 * updates: mirrors.aliyun.com
Resolving Dependencies
……
Installed:
  perl-Digest-MD5.x86_64 0:2.52-3.el7                                                                                                                                                            

Complete!
[root@localhost ~]# ./pt-query-digest /var/lib/mysql/localhost-slow.log 
#该工具执行日志分析的用户时间,系统时间,物理内存占用大小,虚拟内存占用大小
# 100ms user time, 60ms system time, 19.24M rss, 175.66M vsz
#工具执行时间
# Current date: Mon Nov 23 13:24:52 2020
#运行分析工具的主机名
# Hostname: localhost.localdomain
#被分析的文件名
# Files: /var/lib/mysql/localhost-slow.log
#语句总数量,唯一的语句数量,QPS,并发数
# Overall: 16 total, 5 unique, 0.00 QPS, 0.00x concurrency _______________
#日志记录的时间范围
# Time range: 2020-11-23T01:19:36 to 2020-11-23T10:59:43
#属性               总计      最小    最大     平均    95% 	 标准    中等
# Attribute          total     min     max     avg     95%  stddev  median
# ============     ======= ======= ======= ======= ======= ======= =======
#语句执行时间
# Exec time            28s      1s      3s      2s      2s   439ms      2s
#锁占用时间
# Lock time           30ms    90us    10ms     2ms     7ms     3ms   144us
#发送到客户端的行数
# Rows sent             16       1       1       1       1       0       1
#select语句扫描行数
# Rows examine      64.00M   4.00M   4.00M   4.00M   4.00M       0   4.00M
#查询的字符数
# Query size         1.40k      79      94   89.31   92.72    6.84   92.72

# Profile
# Rank Query ID                          Response time Calls R/Call V/M   
# ==== ================================= ============= ===== ====== ===== 
#    1 0x540AF2BCF2C5367E13CD2028259D... 17.3185 61.2%     9 1.9243  0.13 SELECT ums_member_statistics_info_slow
#    2 0x6CEE5E9C52ECE04F9E6DDE07294F...  5.1490 18.2%     3 1.7163  0.00 SELECT ums_member_statistics_info_slow
#    3 0x68FF704A8D7486239579992F4BA6...  3.2095 11.3%     2 1.6047  0.13 SELECT ums_member_statistics_info_slow
#    4 0xE333451ECC5E585FA5E08C9ADC5D...  1.3818  4.9%     1 1.3818  0.00 SELECT ums_member_statistics_info_slow
# MISC 0xMISC                             1.2537  4.4%     1 1.2537   0.0 <1 ITEMS>

# Query 1: 0.00 QPS, 0.00x concurrency, ID 0x540AF2BCF2C5367E13CD2028259D8707 at byte 77767
# This item is included in the report because it matches --limit.
# Scores: V/M = 0.13
# Time range: 2020-11-23T01:19:49 to 2020-11-23T10:59:43
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count         56       9
# Exec time     61     17s      1s      3s      2s      3s   493ms      2s
# Lock time     78    23ms    90us    10ms     3ms    10ms     4ms   131us
# Rows sent     56       9       1       1       1       1       0       1
# Rows examine  56  36.00M   4.00M   4.00M   4.00M   4.00M       0   4.00M
# Query size    59     846      94      94      94      94       0      94
# String:
#数据库
# Databases    test001
#客户端IP
# Hosts        192.168.170.1
#执行用户
# Users        root
#展示查询时间分布,此处是大部分在1s左右
# Query_time distribution
#   1us
#  10us
# 100us
#   1ms
#  10ms
# 100ms
#    1s  ################################################################
#  10s+
# Tables
#    SHOW TABLE STATUS FROM `test001` LIKE 'ums_member_statistics_info_slow'\G
#    SHOW CREATE TABLE `test001`.`ums_member_statistics_info_slow`\G
# EXPLAIN /*!50100 PARTITIONS*/
SELECT COUNT(*) id FROM ums_member_statistics_info_slow WHERE consume_amount =1  LIMIT 0, 1000\G
……
#此处省略一些
[root@localhost ~]# ^C
[root@localhost ~]# 

安装pt-query-digestn需要perl-CPAN perl-Time-HiRes perl-Digest-MD5三个模块

# Profile
# Rank Query ID                          Response time Calls R/Call V/M   
# ==== ================================= ============= ===== ====== ===== 
#    1 0x540AF2BCF2C5367E13CD2028259D... 17.3185 61.2%     9 1.9243  0.13 SELECT ums_member_statistics_info_slow
#    2 0x6CEE5E9C52ECE04F9E6DDE07294F...  5.1490 18.2%     3 1.7163  0.00 SELECT ums_member_statistics_info_slow
#    3 0x68FF704A8D7486239579992F4BA6...  3.2095 11.3%     2 1.6047  0.13 SELECT ums_member_statistics_info_slow
#    4 0xE333451ECC5E585FA5E08C9ADC5D...  1.3818  4.9%     1 1.3818  0.00 SELECT ums_member_statistics_info_slow
# MISC 0xMISC                             1.2537  4.4%     1 1.2537   0.0 <1 ITEMS>

输出格式的解释具体如下。

  • Rank:所有查询日志分析完毕后,此查询的排序。
  • Query ID:查询的标识字符串。可以搜索这个字符串以快速定位到慢查询语句。
  • Response time:总的响应时间,以及总占比,应优化占比较高的查询,对于比例较小的查询一般可以忽略,不进行优化。
  • Calls:查询被调用执行的次数。
  • R/Call:每次执行的平均响应时间。
  • Apdx:应用程序的性能指数得分,响应时间越长,得分越低。
  • V/M:响应时间的方差均值比。可说明样本的分散程度,这个值越大,往往是越值得考虑优化的对象。
  • Item:查询的简单显示,包含了查询涉及的表。对于报告中的如下输出,我们可以利用偏移量到慢查询日志里定位具体的sql语句。
# Query 1: 0.00 QPS, 0.00x concurrency, ID 0x540AF2BCF2C5367E13CD2028259D8707 at byte 77767

定位到具体日志的方法,根据字节定位到具体慢SQL的位置

[root@localhost ~]# tail -c +77767 /var/lib/mysql/localhost-slow.log |head
# Time: 2020-11-23T02:01:29.356794Z
# User@Host: root[root] @  [192.168.170.1]  Id:     4
# Query_time: 2.846838  Lock_time: 0.000133 Rows_sent: 1  Rows_examined: 4194304
SET timestamp=1606096889;
SELECT COUNT(*) id FROM ums_member_statistics_info_slow WHERE consume_amount =1  LIMIT 0, 1000;
/usr/sbin/mysqld, Version: 5.7.31-log (MySQL Community Server (GPL)). started with:
Tcp port: 0  Unix socket: /var/lib/mysql/mysql.sock
Time                 Id Command    Argument
# Time: 2020-11-23T02:09:21.682141Z
# User@Host: root[root] @  [192.168.170.1]  Id:     4
[root@localhost ~]# 

此工具还能生成执行计划的SQL语句,可以根据生成的计划进行确定调优

用法示例:

  1. 重定向到新的日志文件
./pt-query-digest  /var/lib/mysql/localhost-slow.log > slow_report1.log
  1. 分析最近12小时内的查询:
./pt-query-digest  --since=12h  /var/lib/mysql/localhost-slow.log > slow_report2.log
  1. 分析指定时间范围内的查询:
./pt-query-digest /var/lib/mysql/localhost-slow.log --since '2020-11-21 09:30:00' --until '2020-11-23 10:00:00'> slow_report3.log
  1. 分析指含有select语句的慢查询
./pt-query-digest --filter '$event->{fingerprint} =~ m/^select/i' /var/lib/mysql/localhost-slow.log> slow_report4.log
  1. 针对某个用户的慢查询
./pt-query-digest --filter '($event->{user} || "") =~ m/^root/i' /var/lib/mysql/localhost-slow.log> slow_report5.log
  1. 查询所有所有的全表扫描或full join的慢查询
./pt-query-digest --filter '(($event->{Full_scan} || "") eq "yes") ||(($event->{Full_join} || "") eq "yes")' /var/lib/mysql/localhost-slow.log> slow_report6.log
  1. 分析tcpdump的输出。

分析执行SQL的频率,一般在高峰期取样,一定要记得关闭tcpdump,因为生成的文件可能会很大。首先运行命令。

捕获tcp日志

[root@localhost ~]# nohup  tcpdump -i eno16777736 port 3306 -s 65535 -x -nn -q -tttt > db_sql_new.log &

通过tcp包获取查询语句

[root@localhost ~]# ./pt-query-digest --type=tcpdump --watch-server 192.168.170.129:3306 db_sql_new.log > report_to_develper.rtf
  1. 分析genlog日志
[root@localhost ~]# ./pt-query-digest  --type genlog /var/lib/mysql/localhost.log 

genlog开启参见

  1. binlog日志分析
[root@localhost ~]# mysqlbinlog /var/lib/mysql/mysql-bin.000429 > /root/binlog.sql
[root@localhost ~]# ./pt-query-digest  --type binlog /root/binlog.sql

日志分析有很多种办法,最终找到根源所在即可,希望见到mysql慢的问题,能有个清晰的思路。

MySQL慢查询(二) - pt-query-digest详解慢查询日志

分析SQL语句

找到SQL语句之后对SQL进行分析,一般使用EXPLAIN工具进行分析查看执行计划
找到慢SQL在select之前加上EXPLAIN,即可得到执行计划。
类似于

explain select ……;

EXPLAIN命令还有如下两种变体。

EXPLAIN EXTENDED SELECT……
以上命令将执行计划“反编译”成SELECT语句,运行SHOW WARNINGS可得到被MySQL优化器优化后的查询语句。
EXPLAIN PARTITIONS SELECT……
以上命令用于分区表的EXPLAIN命令。 

创建数据

-- 创建user表
create table user(
    id int,
    name varchar(20),
    role_id int,
    primary key(id)
)engine=innodb default charset=utf8;
-- 创建role表
create table role(
    id int,
    name varchar(20),
    primary key(id)
)engine=innodb default charset=utf8;	
mysql> explain select * from user;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> 
  1. id

id包含一组数字,表示查询中执行SELECT子句或操作表的顺序。
如果id相同,则执行顺序由上至下,例如:

-- 左关联
mysql> explain select * from user a left join user b on a.id=b.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref          | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+
|  1 | SIMPLE      | a     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL         |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | b     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test001.a.id |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
-- 右关联
mysql>  explain select * from user a right join user b on a.id=b.id;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref          | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+
|  1 | SIMPLE      | b     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL         |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | a     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test001.b.id |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+
2 rows in set, 1 warning (0.01 sec)

mysql> 
通过left join 和 right join 验证;id一样(注意执行计划的table列),left join 先扫描a表,再扫描b表;right join 先扫描b表,再扫描a表

如果id不同,id越大优先级越高,越先被执行

mysql> explain select * from user where role_id=(select id from role where name='开发');
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
|  2 | SUBQUERY    | role  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> 
我们编写查询角色为开发的用户;可以知道先查询角色name为开发角色id,查询序列号为2;再根据角色id查询用户,查询序列号为1;
  1. select_type

select_type表示查询中每个SELECT子句的类型(是简单还是复杂)。输出结果类似如下,下面是对select_type的详细说明。

  • SIMPLE:查询中不包含子查询或UNION。
mysql> explain select * from user;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> 
  • 查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY。
mysql> explain select * from user where role_id=(select id from role where name='开发');
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
|  2 | SUBQUERY    | role  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

mysql> 
  • 在SELECT或WHERE列表中若包含了子查询,则该子查询被标记为SUBQUERY。
mysql> desc select * from user where role_id=(select id from role where name='开发');
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where |
|  2 | SUBQUERY    | role  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

mysql> 
  • 从UNION表中获取结果的SELECT将被标记为UNION RESULT。

mysql> desc select * from user where name='Java' union select * from user where role_id=1;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | user       | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where     |
|  2 | UNION        | user       | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where     |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL  | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)

mysql> 
  • DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)
mysql> desc select * from user a 
    ->      where id in ( 
    ->          select b.id from user b where b.id=a.id union 
    ->          select c.id from role c where c.id=a.role_id 
    ->      );
+----+--------------------+------------+------------+--------+---------------+---------+---------+-------------------+------+----------+--------------------------+
| id | select_type        | table      | partitions | type   | possible_keys | key     | key_len | ref               | rows | filtered | Extra                    |
+----+--------------------+------------+------------+--------+---------------+---------+---------+-------------------+------+----------+--------------------------+
|  1 | PRIMARY            | a          | NULL       | ALL    | NULL          | NULL    | NULL    | NULL              |    2 |   100.00 | Using where              |
|  2 | DEPENDENT SUBQUERY | b          | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test001.a.id      |    1 |   100.00 | Using where; Using index |
|  3 | DEPENDENT UNION    | c          | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test001.a.role_id |    1 |   100.00 | Using where; Using index |
| NULL | UNION RESULT       | <union2,3> | NULL       | ALL    | NULL          | NULL    | NULL    | NULL              | NULL |     NULL | Using temporary          |
+----+--------------------+------------+------------+--------+---------------+---------+---------+-------------------+------+----------+--------------------------+
4 rows in set, 3 warnings (0.00 sec)

mysql>
  • 在FROM列表中包含的子查询将被标记为DERIVED(衍生)。
mysql> desc select * from ( select * from user where name='Java' union select * from user where role_id=1 ) a;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | <derived2> | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | NULL            |
|  2 | DERIVED      | user       | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where     |
|  3 | UNION        | user       | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where     |
| NULL | UNION RESULT | <union2,3> | NULL       | ALL  | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
4 rows in set, 1 warning (0.00 sec)

mysql> 
  • 若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在FROM子句的子查询中,则外层SELECT将被标记为DERIVED。

mysql> desc select * from ( select * from user where name='Java' union select * from user where role_id=1 ) a;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | <derived2> | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | NULL            |
|  2 | DERIVED      | user       | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where     |
|  3 | UNION        | user       | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where     |
| NULL | UNION RESULT | <union2,3> | NULL       | ALL  | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
4 rows in set, 1 warning (0.00 sec)

mysql> 

MySQL——执行计划

不管是DBA、开发、还是运维人员,对Linux命令和诊断工具要熟练掌握,等到用的时候再去学习,为时已晚。特别是现在对性能要求巨高的情况下,一定要掌握好分析性能的命令。

参考资料:
MySQL DBA 修炼之道
MySQL慢查询 - 开启慢查询.
如何分析Mysql慢SQL

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值