MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志

目录

1.生产中SQL优化步骤

2.查询优化

(1)永远小表驱动大表

(2)order by关键字优化

1)order by子句尽量使用index方式来排序,避免使用filesort方式排序

2)双路排序和单路排序

 3)order by排序总结

(3)group by关键字优化

3.慢查询日志

(1)慢查询是什么

(2)慢查询怎么用?

1)说明

2)查看是否开启以及如何开启

3)开启慢日志查询以后,什么样的日志才会记录到慢查询日志里面?

4)演示慢查询

(3)日志分析工具mysqldumpslow


1.生产中SQL优化步骤

  • 1.观察,至少跑一天,看看生产的慢SQL情况
  • 2.开启慢查询日志,设置阙值,比如超过5秒钟的就是慢SQL,并将它抓取出来
  • 3.explain+慢查询SQL分析
  • 4.show profile查询SQL在mysql服务器里面的执行细节和生命周期情况
  • 5.SQL数据库服务器的参数调优

2.查询优化

(1)永远小表驱动大表

这里类似于嵌套循环,如下

//第一种情况
for(int i=2;...)
{
    for(int j=1000000;...)
    {

    }
}

=================================

//第二种情况
for(int i=1000000;...)
{
    for(int j=2;...)
    {

    }
}

分析:

第一种情况:需要与数据库2次连接和释放的消耗,每次做1000000次的查询

第二种情况:需要与数据库建立1000000次连接和释放的消耗,每次做2次查询,显然这种方式,严重影响性能

in和exists分析

select *  from A where id in (select id from B)

等价于

for select id from B
for select * from A where A.id=B.id

当B表的数据集小于A表的数据集时,用in优于exists

select *  from A where exists (select 1 from B where B.id=A.id)

等价于

for select * from A
for select * from B where B.id=B.id

当A表的数据集小于B表的数据集时,用exists优于in

注意:A表和B表的id字段应该建立索引

Exists

SELECT...FROM table WHERE EXISTS (子查询);

该语法可以理解为:将主查询的数据,放到子查询做条件验证,根据验证结果(TRUE或FALSE)来决定主查询的数据结果是否得以保留

提示:

1.EXISTS(子查询)只返回TRUE或FALSE,因为子查询中的SELECT*也可以是SELECT 1或SELECT 'X',官方说法是实际执行时会忽略SELECT清单,因此没有区别

2.EXISTS子查询的实际执行过程可能经过了优化而不是我们理解上的逐条对比,如果担忧效率问题,可进行实际检验以确定是否有效率问题

3.EXISTS子查询往往也可以用条件表达式,其他子查询或者JOIN来替代,何种最优需要具体问题具体分析

 (2)order by关键字优化

1)order by子句尽量使用index方式来排序,避免使用filesort方式排序

建表

CREATE TABLE tblA(
	age INT,
	birth TIMESTAMP NOT NULL
);

INSERT INTO tblA(age,birth)VALUES(22,NOW());
INSERT INTO tblA(age,birth)VALUES(22,NOW());
INSERT INTO tblA(age,birth)VALUES(22,NOW());

建立索引

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第1张图片

接下来的关注点为会不会产生filesort

1)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第2张图片

order by后排序的字段顺序是按照索引建立的顺序进行,无filesort

2)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第3张图片

order by后排序的字段顺序是按照索引建立的顺序进行,无filesort

3)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第4张图片

order by后排序的字段顺序不是按照索引建立的顺序进行,跳过了带头大哥age,Using filesort

4)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第5张图片

order by后排序的字段顺序与索引建立的顺序相反,Using filesort

5)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第6张图片

order by后排序的字段顺序不是按照索引建立的顺序进行,跳过了带头大哥age,Using filesort

6)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第7张图片

order by后排序的字段顺序不是按照索引建立的顺序进行,跳过了带头大哥age,Using filesort

7)

order by后排序的字段顺序是按照索引建立的顺序进行,无filesort

8)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第8张图片

建好索引的排列顺序默认按照升序排列,而上图order by后的birth字段要求降序排列,建好的索引用不上,mysql只能进行文件内排序

总结:mysql支持两种方式的排序:FileSort和Index,Index效率高,即mysql扫描索引本身完成排序,FileSort效率较低

order by满足以下两种情况,会使用Index方式排序:

  • order by子句使用索引最左前缀
  • 使用where子句和order by子句条件列组合满足索引最左前缀

2)双路排序和单路排序

尽可能在索引列上完成排序操作,遵循索引创建的最左前缀

如果不在索引列上,filesort有两种算法,mysql就要启动双路排序和单路排序

双路排序:

  • mysql4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据
  • 读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出
  • 从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段

取一批数据,要对磁盘进行两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法:

单路排序:

从磁盘读取查询需要的所有列,按照order by列在buffer对他们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据,并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中

由于单路排序是后出的,总体而言好过双路,但是单路有以下问题:

  • 在sort_buffer中,单路排序比多路排序要多占用很多空间,因为单路排序是把所有字段都取出,所以有可能取出的数据的总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp,多路合并),排完后再取sort_buffer容量大小,再排.....从而多次I/O
  • 本来想省一次I/O操作,反而导致大量的I/O操作,反而得不偿失

优化策略:

  • 增大sort_buffer_size的设置
  • 增大max_length_for_sort_data参数的设置

提高order by的速度:

1.order by时select * 是一个大忌只查询需要的字段,这点非常重要,在这里的影响是:

  • (1)当查询的字段大小的总和小于max_length_for_data而且排序字段不是TEXT|BLOB类型时,会用改进后的算法——单路排序,否则,用老算法——多路排序
  • (2)两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次IO,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size

2.尝试提高sort_buffer_size

不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程

3.尝试提高max_length_for_sort_data

提高这个参数,会增加用改进算法的概率,但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率

 3)order by排序总结

  • mysql两种排序方式:文件排序或扫描有序索引排序
  • mysql能为排序与查询使用相同的索引

示例:

index a_b_c(a,b,c)
  • 1.order by能使用索引最左前缀
——order by a

——order by a,b

——order by a,b,c

——order by a DESC,b DESC,c DESC
  • 2.如果where使用索引的最左前缀定义为常量,则order by能使用索引
——where a=const order by b,c

——where a=const and b=const order by c

——where a=const order by b,c

——where a=const and b>const order by b,c
  • 3.不能使用索引进行排序
——order by a ASC,b DESC,c DESC      //排序不一致

——where g=const order by b,c      //丢失a索引

——where a=const order by c      //丢失b索引

——where a=const order by a,d      //d不是索引的一部分

——where a in (...) order by b,c      //对于排序来说,多个相等条件也是范围查询

(3)group by关键字优化

group by实质是先排序后进行分组,遵循索引建的最佳左前缀

当无法使用索引列,增大max_length_for_sort_data参数的设置,增加sort_buffer_size参数的设置

group by的优化几乎和上述的order by一致,不过,group by多了如下:

where高于having,能写在where限定的条件就不要去having限定了

3.慢查询日志

(1)慢查询是什么

  • mysql的慢查询日志是mysql提供的一种日志记录,它用来记录在mysql中响应时间超过阙值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中
  • 具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中,long_query_time的默认值为10,意思是运行10秒以上的语句
  • 由它来查看哪些SQL超出了我们的最大忍耐时间值,比如一条sql执行超过5s,我们就算慢SQL,希望能收集超过5s的sql,结合之前explain进行全面分析

(2)慢查询怎么用?

1)说明

默认,mysql数据库没有开启慢查询日志,需要我们手动来设置这个参数

当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能的影响,慢查询日志支持将日志记录写入文件

2)查看是否开启以及如何开启

//查看是否开启
SHOW VARIABLES LIKE '%slow_query_log%';

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第9张图片

//下列方法只对当前数据库有效,数据库重启后失效
set GLOBAL slow_query_log=1;

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第10张图片

以上方法只对当前数据库生效,数据库重启后失效

如果要永久生效,就必须修改配置文件my.cnf(其它系统变量也是如此)

修改my.cnf,[mysqld]下增加或修改参数

slow_query_log=1
slow_query_log_file=/var/lib/mysql/日志文件名.log

//关于慢查询的参数slow_query_log_file,它指定慢查询存放的路径,
//系统默认会给一个缺省的文件host_name-slow.log(如果没有指定参数slow_query_log_file的话)

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第11张图片

修改之后,重启mysql服务器

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第12张图片

3)开启慢日志查询以后,什么样的日志才会记录到慢查询日志里面?

这个是由参数long_query_time控制,默认情况下long_query_time的值为10秒

命令:

show variables like 'long_query_time';

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第13张图片

同样可以使用命令修改,也可以在my.cnf配置文件中的参数里面设置

假如运行时间正好等于long_query_time的情况,并不会被记录下来,也就是说,在mysql的源码里是判断大于long_query_time,而非大于等于

4)演示慢查询

设置慢的阙值时间为3秒钟

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第14张图片

为什么修改之后再次查看long_query_time后无变化?

方式1:需要重新连接或新开一个会话show variables like 'long_query_time';才能看到变化

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第15张图片

方式2:在原会话中使用show global variables like 'long_query_time';

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第16张图片

模拟一个执行4秒的慢SQL

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第17张图片

查询上述我们设置的慢日志文件

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第18张图片

可以看到慢的SQL语句被记录在日志中,并且在日志中还有关于它的详细信息

查询当前系统中有多少条慢查询记录

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第19张图片

(3)日志分析工具mysqldumpslow

在生产环境中,要手工分析日志,查找、分析SQL,显然是个体力活,mysql提供了日志分析工具 mysqldumpslow

查询mysqldumpslow的帮助信息

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第20张图片

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第21张图片

使用参考示例:

1.得到返回记录集最多的10个SQL

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第22张图片

2.得到访问次数最多的10个SQL

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第23张图片

3.得到按照时间排序的前10条里面含有左连接的查询语句

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第24张图片

4.另外建议在使用这些命令时结合|和more使用,否则有可能出现爆屏情况

MySQL之SQL性能(3)——查询截取分析(1)——查询优化、慢查询日志_第25张图片

你可能感兴趣的:(MySQL)