本文目录
- 一、前言
- 二、慢查询日志定位执行慢的 SQL
-
- 2.1.开启慢查询日志
- 2.2.设置慢查询的时间阈值
- 2.3. 慢查询日志介绍
-
- 模拟慢查询
- 慢查询日志
- 2.4.日志分析工具mysqldumpslow
- 三、查询优化神器EXPLAIN命令
-
- 3.1. id字段
- 3.2. select_type字段
- 3.3. table字段
- 3.4. partitions字段
- 3.5. type字段
- 3.6. possible_keys字段
- 3.7. key字段
- 3.8. rows字段
- 3.9. key_len字段
- 3.10. Extra字段
- 总结
- 四、SQL性能分析show profile命令
- 五、总结
一、前言 我们在做
SQL
优化的时候,应该从哪几方面定位SQL执行慢的原因呢?是索引设计的问题?参数配置的问题?还是需要扩容了呢?本文介绍了比较常用的三种工具来定位和分析慢SQL,本文主要内容如下:
- 通过慢查询日志定位执行慢的 SQL;
- 使用
EXPLAIN
分析该 SQL 语句是否使用到了索引,以及具体的数据表访问方式; - 使用
SHOW PROFILE
进一步分析SQL的每一步执行时间以及CPU、IO等资源使用情况。
long_query_time
参数值的SQL。long_query_time
的默认值为10秒,默认情况下执行超过10s的SQL语句,会被记录到慢查询日志中。2.1.开启慢查询日志 在使用前,我们需要先看下慢查询是否已经开启,使用下面这条命令查看是否开启:
show variables like '%slow_query_log%';
文章图片
slow_query_log=OFF
,表示未开启。 slow_query_log=ON
表示慢查询已经开启,如果未开启,通过以下命令开启:set global slow_query_log='ON';
开启后,使用
show variables like '%slow_query_log%'
再来看下是否开启文章图片
【mysql|MySQL性能分析工具——如何快速定位SQL执行慢的原因()】
slow_query_log_file
是慢查询日志文件目录。Mysql支持文件和数据库表两种日志存储方式,默认将日志存储在文件中,使用下面这条命令查看存储方式:
show variables like '%log_output%';
文章图片
可以修改为存储到数据库表,这样日志信息就会被写入到
mysql.slow_log
表中set global log_output='TABLE';
文章图片
MySQL数据库同时支持两种日志存储方式,配置的时候以逗号隔开即可
set global log_output='TABLE,FILE';
日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,因此对于需要启用慢查询日志,又需要能够获得更高的系统性能,那么建议优先记录到文件。
2.2.设置慢查询的时间阈值
long_query_time
的默认值为10秒,通过以下命令可以查看慢查询的时间阈值show variables like '%long_query_time%';
文章图片
如果我们想修改
long_query_time
参数值,通过以下命令即可,假如我们把long_query_time
值设置为1。set global long_query_time = 1;
注意:修改完再次执行show variables like ‘%long_query_time%’,发现还是默认的10S,是不是没有修改成功呢?其实已经修改成功了,此时只需要关闭当前连接,再重新连接就可以了。使用
set global slow_query_log=1
开启了慢查询日志只对当前数据库生效,如果MySQL重启后则会失效。如果要永久生效,需要修改my.cnf
配置文件,配置文件如下:[mysqld]
slow_query_log = 1# 开启慢查询
slow_query_log_file = /data/mysql/logs/slow.log# 慢查询日期路径
long_query_time = 1# 慢查询时间阈值
log_timestamps = SYSTEM
log_output = FILE
2.3. 慢查询日志介绍 模拟慢查询
如果自己mysql上没有慢查询,可以通过
sleep(N)
函数来模拟慢查询操作,比如下面这条模拟慢查询的语句SELECT *, sleep(3) FROM tb_user WHERE id<4
注意:
sleep(N)
函数表示,每返回一行数据经过WHERE条件判断后,都会触发Sleep函数,比如上面的SQL
语句,返回3条数据,每条阻塞3秒,查询时间总共是9秒。文章图片
慢查询日志
有了慢查询日志,可以使用以下命令获取有多少条日志
show global status like '%Slow_queries%';
文章图片
可以看到现在有两条慢查询,接下来我们来看下慢查询日志具体记录哪些内容
# Time: 2022-01-22T12:56:00.070149Z
# User@Host: root[root] @ localhost []Id:8
# Query_time: 4.006927Lock_time: 0.000074 Rows_sent: 4Rows_examined: 4
SET timestamp=1642856160;
SELECT *, sleep(1) FROM tb_user WHERE id<5;
第一行记录的是该条 SQL 语句执行的时间;
第二行记录的是执行该SQL语句的用户和 IP 以及链接 id;
第三行的几个字段解释如下:
- Query_time: duration 语句执行时间,以秒为单位。
- Lock_time: duration 获取锁的时间(以秒为单位)。
- Rows_sent: 发送给 Client 端的行数。
- Rows_examined: 服务器层检查的行数(不计算存储引擎内部的任何处理)
第五行就是具体的慢SQL。也是我们需要优化的SQL。
2.4.日志分析工具mysqldumpslow MySQL 提供了
mysqldumpslow
工具统计慢查询日志。mysqldumpslow
命令的参数如下:- -s:采用 order 排序的方式,排序方式可以有以下几种。分别是 c(访问次数)、t(查询时间)、l(锁定时间)、r(返回记录)、ac(平均查询次数)、al(平均锁定时间)、ar(平均返回记录数)和 at(平均查询时间)。其中 at 为默认排序方式。
- -t:返回前 N 条数据 。
- -g:后面可以是正则表达式,对大小写不敏感。
mysqldumpslow -s r -t 3 /var/lib/mysql/a7bb95cee15a-slow.log
得到访问次数最多的5个SQL
mysqldumpslow -s c -t 5/var/lib/mysql/a7bb95cee15a-slow.log
得到按照时间排序的前5条里面含有左连接的查询语句。
mysqldumpslow -s t -t 10 -g “left join”/var/lib/mysql/a7bb95cee15a-slow.log
另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。
mysqldumpslow -s r -t 5/var/lib/mysql/a7bb95cee15a-slow.log | more
三、查询优化神器EXPLAIN命令 MySQL 提供的
EXPLAIN
命令,它可以对 SELECT
语句进行分析,并输出 SELECT 执行的详细信息,开发人员可以根据输出的信息对SQL进行有针对性的优化。EXPLAIN
可以帮助我们了解数据表的读取4,l;
’]m np[ohfg]'顺序、SELECT 子句的类型、数据表的访问类型、可使用的索引、实际使用的索引、使用的索引长度、上一个表的连接匹配条件、被优化器查询的行的数量olhb.;
/及额外的信息(比如是否使用了外部排序,是否使用了临时表等)等。EXPLAIN
使用方式如下,EXPLAIN 命令的输出内容如下:文章图片
下面对
EXPLAIN
命令的输出各个字段进行说明3.1. id字段 select查询的序列号,表示查询中执行select子句或操作表的顺序。
SQL
执行的顺序是根据 id 从大到小执行的,也就是 id 越大越先执行,当 id 相同时,从上到下执行。比如下面这个
SQL
语句id一样,执行顺序按照从上到下执行。EXPLAIN SELECT u.name,o.title FROMtb_user u JOIN tb_order o ON u.id=o.user_id
文章图片
下面的这条
SQL
语句,可以看到子查询的id为2,所以这条语句先执行是子查询EXPLAIN SELECT * FROM tb_order WHERE user_id = (SELECT id FROM tb_user WHERE id=1)
文章图片
3.2. select_type字段 表示查询的类型。常用的值如下表:
select_type字段值 | 说明 |
---|---|
SIMPLE | 表示查询语句不包含子查询或union |
PRIMARY | 表示此查询是最外层的查询 |
UNION | 表示此查询是UNION的第二个或后续的查询 |
DEPENDENT UNION | UNION中的第二个或后续的查询语句,使用了外面查询结果 |
UNION RESULT | UNION的结果 |
SUBQUERY | SELECT子查询语句 |
SIMPLE
,表示我们的查询没有子查询也没用到UNION查询。3.3. table字段 输出行所引用的表的名称。
3.4. partitions字段 该列显示的为分区表命中的分区情况。非分区表该字段为空(null)。
3.5. type字段 type字段表示存储引擎查询数据时采用的方式。这个也是我们平时做
SQL
优化重点关注的信息,通过它可以判断出查询是全表扫描还 是基于索引的部分扫描。type 常用属性值如下表所示:type字段值 | 说明 |
---|---|
ALL |
全表扫描,性能最差。 |
index |
表示基于索引的全表扫描,先扫描索引再扫描全表数据。 |
range |
表示使用索引范围查询。使用>、>=、<、<=、in等等。 |
ref |
表示采用了非唯一索引,或者是唯一索引的非唯一性前缀 |
eq_ref |
一般情况下出现在多表join查询,表示前面表的每一个记录,都只能匹配后面表的一 行结果。 |
const |
表最多有一个匹配行,因为只有一行,所以这一行中列的值可以被优化器视为常量 |
system |
system 类型一般用于 MyISAM 或 Memory 表,属于 const 类型的特例 |
type
字段值,执行效率从上至下依次增强。效率从低到高依次为 all < index < range < index_merge < ref < eq_ref < const/system
。下面对type字段常用的类型做详细的说明
-
all
是最坏的情况,因为采用了全表扫描的方式。
-
index
和 all 差不多,只不过index
对索引表进行全扫描,这样做的好处是不再需要对数据进行排序,但是开销依然很大。如果我们在 extra 列中看到 Using index,说明采用了索引覆盖,也就是索引可以覆盖所需的 SELECT 字段,就不需要进行回表,这样就减少了数据查找的开销。
下面通过一个例子,来对index进行说明:
比如我们有个tb_user
表,对name和phone字段建立联合索引:
CREATE INDEX name_phone_index on tb_user(`name`,phone)
然后对数据表中的 name、phone 字段进行查询,EXPLAIN 执行计划如下:
文章图片
从EXPLAIN执行结果中可以看到,这条
SQL
使用了index的方式,key列采用了联合索引(name_phone_index),进行了全表扫描。Extral
列为 Using index,告诉我们索引可以覆盖 SELECT 中的字段,也就不需要回表查询了。-
range
表示采用了索引范围扫描。比如下面这条SQL语句
select * from tb_user where id>=1 and id<=5;
文章图片
从
range
这一级别开始,索引的作用会越来越明显,因此我们需要尽量让 SQL
查询可以使用到 range 这一级别及以上的 type 访问方式。-
ref
类型表示采用了非唯一索引,或者是唯一索引的非唯一性前缀。
比如我们对tb_order
表user_id
字段创建索引
CREATE INDEX user_id_index on tb_order(user_id)
然后查询user_id等于1的订单信息,EXPLAIN 执行计划如下:
EXPLAIN SELECT title,order_datetime FROM tb_order WHERE user_id=1
文章图片
这里
user_id
为普通索引,因此采用的访问类型是 ref,同时在 ref 列中显示 const
,表示连接匹配条件是常量,用于索引列的查找。-
eq_ref
类型是使用主键或唯一索引时产生的访问方式,通常使用在多表联查中。
比如我们对tb_order
表和tb_user
进行join关联查询
EXPLAIN SELECT o.title,o.order_datetime,u.name FROM tb_order o JOIN tb_useru ON o.user_id=u.id
EXPLAIN 执行计划如下:
文章图片
-
const
类型表示我们使用了主键或者唯一索引(所有的部分)与常量值进行比较。
比如根据主键id查找某一个用户
文章图片
在做优化的时候,最好可以使用到 range 这一级别及以上的 type 访问方式,如果只使用到了
all
或者 index
这一级别的访问方式,我们可以从 SQL
语句和索引设计的角度上进行改进。3.6. possible_keys字段 表示查询时可能能使用到的索引。实际并不一定会真正使用。
3.7. key字段 表示查询时真正使用到的索引,显示的是索引名称。
3.8. rows字段
MySQL
查询优化器会根据统计信息,估算SQL
要查询到结果需要扫描多少行记录。原则上rows是 越少效率越高,可以直观的了解到SQL
效率高低。3.9. key_len字段 表示查询使用了索引的字节数量。可以判断是否全部使用了组合索引。
key_len的计算规则如下:
- 字符串类型
字符串长度跟字符集有关,常见编码长度:gbk=2、utf8=3、utf8mb4=4
char(n):n*字符集长度
varchar(n):n * 字符集长度 + 2字节
- 数值类型
TINYINT
:1个字节
SMALLINT
:2个字节
MEDIUMINT
:3个字节
INT、FLOAT
:4个字节
BIGINT、DOUBLE
:8个字节
- 时间类型
DATE
:3个字节
TIMESTAMP
:4个字节
DATETIME
:8个字节
- Using where 表示查询需要通过索引回表查询数据。
- Using index 表示查询需要通过索引,索引就可以满足所需数据。
- Using filesort 表示查询出来的结果需要额外排序,数据量小在内存,大的话在磁盘,因此有
Using filesort
建议优化。 Using temprorary
查询使用到了临时表,一般出现于去重、分组等操作。
EXPLAIN
分析慢SQL的时候,我们主要关注type
字段,最好可以使用到 range 这一级别及以上的 type 访问方式,如果只使用到了 all
或者 index
这一级别的访问方式,我们可以从 SQL
语句和索引设计的角度上进行改进。其次关注
rows
字段,绝大部分rows
小的语句执行一定很快,所以优化语句基本上都是在优化rows。四、SQL性能分析show profile命令
show profile
相比 EXPLAIN
能看到更进一步的执行解析,包括 SQL 都做了什么、所花费的时间等。默认情况下,
show profile
是关闭的,使用下面的命令查看状态mysql> show variables like 'profiling';
文章图片
通过设置
profiling='ON’
来开启 show profile:mysql > set profiling = 'ON';
文章图片
show profile命令只是在本会话内起作用,即无法分析本会话外的语句。开启分析功能后,所有本会话中的语句都被分析(甚至包括执行错误的语句),除了SHOW PROFILE和SHOW PROFILES两句本身。查看当前会话都有哪些 profiles,使用下面这条命令:
mysql > show profiles;
文章图片
使用下面的命令我们可以查看上一个查询的开销:
mysql > show profile;
文章图片
可以给show profile 指定一个 for query id 来查看指定 id 的语句,比如查看Query_ID为4的SQL信息
mysql> show profile for query 4;
还可以给输出添加新的列,取值范围可以如下:
- ALL 显示所有性能信息
- BLOCK IO 显示块IO操作的次数
- CONTEXT SWITCHES 显示上下文切换次数,不管是主动还是被动
- CPU 显示用户CPU时间、系统CPU时间
- IPC 显示发送和接收的消息数量
- PAGE FAULTS 显示页错误数量
- SOURCE 显示源码中的函数名称与位置
- SWAPS 显示SWAP的次数
mysql> show profile cpu,block io,swaps for query 4;
文章图片
通过上面的结果,我们可以弄清楚每一步骤的耗时,以及在不同部分,比如
CPU
、block io
的执行时间,这样我们就可以判断出来 SQL
到底慢在哪里。五、总结 本文介绍了MySQL性能分析常用的三种工具(还有很多性能分析工具),通过慢查询日志定位执行慢的 SQL,然后通过 EXPLAIN 分析该 SQL 语句是否使用到了索引和具体的数据表访问方式是怎样的。如果有需要最后再使用
SHOW PROFILE
进一步了解 SQL 每一步的执行时间,包括CPU 、I/O 等资源的使用情况。通过上面介绍的这三种工具我们可以快速的定位和分析慢SQL,然后做出相应的优化。
参考文档:
《高性能MySQL》
- https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
- https://dev.mysql.com/doc/refman/8.0/en/show-profile.html
- https://www.cnblogs.com/kerrycode/p/5593204.html
推荐阅读
- 数据库|MySQL之DDL操作表
- 数据库|MySql -- 不存在则插入,存在则更新或忽略
- WP查询以获取具有特定term_taxonomy的帖子
- 计算机毕业设计|计算机毕业设计android的图书馆图书借阅座位预订app(源码+系统+mysql数据库+Lw文档)
- android|计算机毕业设计Android安卓医院挂号预约系统软件app(源码+系统+mysql数据库+Lw文档)
- 数据库|计算机毕业设计jsp旅游网站
- MySQL-DML(Data Manipulation Language)详解
- 数据库|MySQL学习
- MySQL数据库|数据库基本概念及MySQL基本命令操作(图文详解)