SQL优化对于一枚程序员来说是至关重要的,并且大部分面试中,都会问道有关sql优化的一 些问题,这里将带着大家学会如何分析sql执行效率,首先要想优化一条sql语句,前提是我们要能够定位到查询慢的sql语句,并对其进行分析,找到慢查询的圆心,然后进行优化。

定位慢 SQL

大家在工作中测试的时候偶尔会碰到查询结果,超过一定时间才返回,这时我们就应该考虑是不是慢查询导致的,接下来让我们看看如果定位慢sql语句

一 通过慢查询日志

如果需要定位到慢查询,一般的方法是通过慢查询日志来查询的,MySQL 的慢查询日志它会记录在 MySQL 中响应时间超过参数 long_query_time(单位秒,默认值 10)设置的值并且扫描记录数不小于min_examined_row_limit(默认值位0)的语句,所以它会帮我们找出查询慢的sql语句。

默认情况下,慢查询日志中并不会记录管理语句,可通过设置参数 log_slow_admin_statements = on 让管理语句中的慢查询也会记录到慢查询的日志中。

默认情况下,也不会记录查询时间不超过 long_query_time 但是不使用索引的语句,可通过配置 log_queries_not_using_indexes = on 让不使用索引的 SQL 都被记录到慢查询日志中(即使查询时间没超过 long_query_time 配置的值)。

通常我们开启慢查询日志一般分为4给步骤:
第一步 开启慢查询日志
首先开启慢查询日志,由参数 slow_query_log 决定是否开启,在 MySQL 的命令行下输入下面的命令:

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

第二步 设置慢查询阀值

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

MySQL 中 long_query_time 的值如何确定呢?

线上业务一般建议把 long_query_time 设置为 1 秒,如果某个业务的 MySQL 要求比较高的 QPS,可设置慢查询为 0.1
秒。发现慢查询及时优化或者提醒开发改写。

一般测试环境建议 long_query_time 设置的阀值比生产环境的小,比如生产环境是 1 秒,则测试环境建议配置成 0.5
秒。便于在测试环境及时发现一些效率低的 SQL。

甚至某些重要业务测试环境 long_query_time 可以设置为
0,以便记录所有语句。并留意慢查询日志的输出,上线前的功能测试完成后,分析慢查询日志每类语句的输出,重点关注
Rows_examined(语句执行期间从存储引擎读取的行数),提前优化。

第三步 确定慢查询日志文件路径
慢查询日志的路径默认是 MySQL 的数据目录

mysql> show global variables like "datadir";
+---------------+------------------------+
| Variable_name | Value                  |
+---------------+------------------------+
| datadir       | /data/mysql/data/3306/ |
+---------------+------------------------+
1 row in set (0.00 sec)

第四步 确定慢查询日志的文件名

mysql> show global variables like "slow_query_log_file";
+---------------------+----------------+
| Variable_name       | Value          |
+---------------------+----------------+
| slow_query_log_file | mysql-slow.log |
+---------------------+----------------+
1 row in set (0.00 sec)

根据上面的查询结果,可以直接查看 /data/mysql/data/3306/mysql-slow.log 文件获取已经执行完的慢查询

[root@mysqltest ~]# tail -n5 /data/mysql/data/3306/mysql-slow.log
Time: 2019-05-21T09:15:06.255554+08:00
User@Host: root[root] @ localhost []  Id: 8591152
Query_time: 10.000260  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
SET timestamp=1558401306;
select sleep(10);

这里对上方的执行结果详细描述一下:

tail -n5:只查看慢查询文件的最后 5
Time:慢查询发生的时间
User@Host:客户端用户和 IP
Query_time:查询时间
Lock_time:等待表锁的时间
Rows_sent:语句返回的行数
Rows_examined:语句执行期间从存储引擎读取的行数

小伙伴们可以使用 pt-query-digest 或者 mysqldumpslow 等工具对慢查询日志进行分析

二 通过 show processlist
有时慢查询正在执行,已经导致数据库负载偏高了,而由于慢查询还没执行完,因此慢查询日志还看不到任何语句。此时可以使用 show processlist 命令判断正在执行的慢查询。show processlist 显示哪些线程正在运行。如果有 PROCESS 权限,则可以看到所有线程。否则,只能看到当前会话的线程。

如果不使用 FULL 关键字,在 info 字段中只显示每个语句的前 100 个字符,如果想看语句的全部内容可以使用 full 修饰(show full processlist)。

mysql> show processlist\G`
`......`
`*************************** 10. row ***************************`
     `Id: 7651833`
   `User: one`
   `Host: 192.168.1.251:52154`
     `db: ops`
`Command: Query`
   `Time: 3`
  `State: User sleep`
   `Info: select sleep(10)`
`......`
`10 rows in set (0.00 sec)`

这里对上面结果解释一下:

Time:表示执行时间
Info:表示 SQL 语句
我们这里可以通过它的执行时间(Time)来判断是否是慢 SQL。

上面讲了如果定位到慢查询,接下来,就让我们看看怎么分析定位到的慢查询了

使用 explain 分析慢查询

我们可以通过 explain、show profile 和 trace 等诊断工具来分析慢查询。

Explain 可以获取 MySQL 中 SQL
语句的执行计划,比如语句是否使用了关联查询、是否使用了索引、扫描行数等。可以帮我们选择更好地索引和写出更优的 SQL.使用方法:在查询语句前面加上 explain 运行就可以了。

为了便于理解,先创建两张测试表,建表及数据写入语句如下:

CREATE DATABASE muke;           /* 创建测试使用的database,名为muke */
use muke;                       /* 使用muke这个database */
drop table if exists t1;        /* 如果表t1存在则删除表t1 */
CREATE TABLE `t1` (             /* 创建表t1 */
  `id` int(11) NOT NULL auto_increment,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_a` (`a`),
  KEY `idx_b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;	
drop procedure if exists insert_t1; /* 如果存在存储过程insert_t1,则删除 */
delimiter ;;
create procedure insert_t1()        /* 创建存储过程insert_t1 */
begin
  declare i int;                    /* 声明变量i */
  set i=1;                          /* 设置i的初始值为1 */
  while(i<=1000)do                  /* 对满足i<=1000的值进行while循环 */
    insert into t1(a,b) values(i, i); /* 写入表t1中a、b两个字段,值都为i当前的值 */
    set i=i+1;                      /* 将i加1 */
  end while;
end;;
delimiter ;                 /* 创建批量写入1000条数据到表t1的存储过程insert_t1 */
call insert_t1();           /* 运行存储过程insert_t1 */
drop table if exists t2;    /* 如果表t2存在则删除表t2 */
create table t2 like t1;    /* 创建表t2,表结构与t1一致 */
insert into t2 select * from t1;   /* 将表t1的数据导入到t2 */

下面尝试使用 explain 分析一条 SQL

mysql> explain select * from t1 where b=100;

在这里插入图片描述
列名 解释
id 查询编号
select_type 查询类型:显示本行是简单还是复杂查询
table 涉及到的表
partitions 匹配的分区:查询将匹配记录所在的分区。仅当使用 partition 关键字时才显示该列。对 于非分区表,该值为 NULL。
type 本次查询的表连接类型
possible_keys 可能选择的索引
key 实际选择的索引
key_len 被选择的索引长度:一般用于判断联合索引有多少列被选择了
ref 与索引比较的列
rows 预计需要扫描的行数,对 InnoDB 来说,这个值是估值,并不一定准确
filtered 按条件筛选的行的百分比
Extra 附加信息
其中 explain 各列都有各种不同的值,这里介绍几个比较重要列常包含的值:包含 select_typ、type 和 Extra。

select_type

SIMPLE 简单查询 (不使用关联查询或子查询)
PRIMARY 如果包含关联查询或者子查询,则最外层的查询部分标记为 primary
UNION 联合查询中第二个及后面的查询
DEPENDENT UNION 满足依赖外部的关联查询中第二个及以后的查询
UNION RESULT 联合查询的结果
SUBQUERY 子查询中的第一个查询
DEPENDENT SUBQUERY 子查询中的第一个查询,并且依赖外部查询
DERIVED 用到派生表的查询
MATERIALIZED 被物化的子查询
UNCACHEABLE SUBQUERY 一个子查询的结果不能被缓存,必须重新评估外层查询的每一行
UNCACHEABLE UNION 关联查询第二个或后面的语句属于不可缓存的子查询

system 查询对象表只有一行数据,且只能用于 MyISAM 和 Memory 引擎的表,这是最好的情况
const 基于主键或唯一索引查询,最多返回一条结果
eq_ref 表连接时基于主键或非 NULL 的唯一索引完成扫描
ref 基于普通索引的等值查询,或者表间等值连接
fulltext 全文检索
ref_or_null 表连接类型是 ref,但进行扫描的索引列中可能包含 NULL 值
index_merge 利用多个索引
unique_subquery 子查询中使用唯一索引
index_subquery 子查询中使用普通索引
range 利用索引进行范围查询
index 全索引扫描
ALL 全表扫描
上表的这些情况,查询性能从上到下依次是最好到最差.

Extra

Using filesort 将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序 explain select * from t1 order by create_time;
Using temporary 需要创建一个临时表来存储结构,通常发生对没有索引的列进行 GROUP BY 时 explain select * from t1 group by create_time;
Using index 使用覆盖索引 explain select a from t1 where a=111;
Using where 使用 where 语句来处理结果 explain select * from t1 where create_time=‘2019-06-18 14:38:24’;
Impossible WHERE 对 where 子句判断的结果总是 false 而不能选择任何数据 explain select * from t1 where 1<0;
Using join buffer (Block Nested Loop) 关联查询中,被驱动表的关联字段没索引 explain select * from t1 straight_join t2 on (t1.create_time=t2.create_time);
Using index condition 先条件过滤索引,再查数据 explain select * from t1 where a >900 and a like “%9”;
Select tables optimized away 使用某些聚合函数(比如 max、min)来访问存在索引的某个字段是 explain select max(a) from t1;

本文主要讲到如何定位慢 SQL:

一种方法是查看慢查询日志
另一种方法是 show process 查看正在执行的 SQL
再讲到通过 explain 分析慢 SQL,explain 会返回很多字段,其中 select_type、type、key、rows、Extra 是重点关注项。

前言SQL优化对于一枚程序员来说是至关重要的,并且大部分面试中,都会问道有关sql优化的一 些问题,这里将带着大家学会如何分析sql执行效率,首先要想优化一条sql语句,前提是我们要能够定位到查询慢的sql语句,并对其进行分析,找到慢查询的圆心,然后进行优化。定位慢 SQL大家在工作中测试的时候偶尔会碰到查询结果,超过一定时间才返回,这时我们就应该考虑是不是慢查询导致的,接下来让我们看看如果定位慢sql语句一 通过慢查询日志如果需要定位到慢查询,一般的方法是通过慢查询日志来查询的,MySQL
DataGrip使用教程 今天给大家介绍一款数据库连接工具,可能你正在使用navicat、workbench、sqlyog、DBeaver等等,这里不做拉踩,没有最好的工具,更没有完美的工具,即便众多连接工具的目标肯定是趋于完美,笔者认为,适合自己的才是最好的,下面给大家介绍一下jetbrain大家族中dataGrip,特色功能很多,下面只是列举了开发中常见的操作,欢迎大家评论补充。 1、下载和安装 https://www.jetbrains.com/datagrip/ 激活方式和IDEA一样
了-凡 2019-10-19 09:39:56 308 收藏 分类专栏: # Navicate使用指南 开发工具使用 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_34326321/article/details/102635135 1.提出问题 2.解决方案 3.顺便补充 4.资料备忘贴 1.提出问题 我这两天一直在纠结表与 What:Pro_BRANCHCNCATASK; Next Date :根据job开始执行时间的实际情况填写,时间格式2013-9-4 上午04:00:00。 Interval: TRUNC(sysdate) +1+4/ (24) TRUNC(sysdate) +1+4/ 
参考链接:https://www.hangge.com/blog/cache/detail_2829.html 日常开发中少不了各种可视化数据库管理工具。如果需要同时能连接多种数据库,大家肯定都会想到 DBeaver、Navicat Premium。本文介绍另一个十分好用且强大的工具:DataGrip。 DataGrip 是 JetBrains 公司推出的管理数据库的产品。对于 JetBrains 公司,开发者肯定都不陌生,IDEA 和 ReSharper 都是这个公司的产品,用户体验非常不错。 DataG
公司要求不能使用盗版软件,心爱的Navicat又买不起,正好有Jetbrains的全家桶授权,就开始尝试使用Datagrip进行数据管理。在界面和操作逻辑上都是称心如意的,但是每次看个数据表或者文档,快的要等五六秒,慢的要等二十几秒,即使表里面只有几条数据。这个时间真的是有点耽误我写bug的效率了。 网上搜索了下,大部分是让把连接属性中的Auto sync选项关掉,但我试了几乎感觉不出来有啥变化,那肯定不是这个问题,JetBrains不可能会犯这么低级的问题。 于是又谷歌了下,在JetB
查看Oracle执行计划的几种方法 一、通过PL/SQL Dev工具     1、直接File->New->Explain Plan Window,在窗口中执行sql可以查看计划结果。其中,Cost表示cpu的消耗,单位为n%,Cardinality表示执行的行数,等价Rows。     2、先执行 EXPLAIN PLAN FOR   select * from tableA where
一、启用慢SQL 开启慢SQL的配置参数 slow_query_log: 该参数表示是否开启慢SQL查询日志。在mysql中,我们可以通过以下命令来查看和修改该变量的状态 1.show variables like ‘%slow_query_log%’ #查询该变量在MySQL中当前的状态。 2.set GLOBAL slow_query_log = ON/OFF #开启慢查询监控ON,关闭慢查询监控OFF 开启general_log执行日志 1.show variables like “gene
3. 查询优化:通过查询优化器对 SQL 语句进行优化,选择最佳的执行计划,提高查询性能。 4. 执行计划分析分析 SQL 查询的执行计划,查看优化器选择的最佳执行计划是否符合预期,有没有潜在的性能问题。 5. 索引分析分析表的索引是否合理,是否能够优化查询性能。 通过以上几个方面的分析,可以有效地提高 SQL 查询的性能和效率,同时避免出现潜在的问题。