Python全栈(三)数据库优化之11.MySQL高级-排序优化、慢查询日志、批量插入数据和Show Profile

一.排序优化

1.定义

即order by优化。
order by子句,尽量使用index方式排序,避免使用filesort方式排序。

2.建表,插入测试数据:

create table tbla(
    age int,
    birth timestamp not null
);

insert into tbla(age,birth) values(22,now());
insert into tbla(age,birth) values(23,now());
insert into tbla(age,birth) values(24,now());

打印

Query OK, 0 rows affected (0.02 sec)

Query OK, 1 row affected (0.01 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

3.建立索引:

create index idx_tbla_agebrith on tbla(age,birth);

打印

Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

4.分析:

explain select * from tbla where age > 30 order by age;

打印

+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | tbla  | NULL       | range | idx_tbla_agebrith | idx_tbla_agebrith | 5       | NULL |    1 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.01 sec)

未出现Using filesort。

explain select * from tbla where age > 30 order by age,birth;

打印

+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | tbla  | NULL       | range | idx_tbla_agebrith | idx_tbla_agebrith | 5       | NULL |    1 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

仍然未出现Using filesort。

explain select * from tbla where age > 30 order by birth;

打印

+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                                    |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
|  1 | SIMPLE      | tbla  | NULL       | range | idx_tbla_agebrith | idx_tbla_agebrith | 5       | NULL |    1 |   100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.00 sec)

此时出现了Using filesort。

explain select * from tbla where age > 30 order by birth,age;

打印

+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                                    |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
|  1 | SIMPLE      | tbla  | NULL       | range | idx_tbla_agebrith | idx_tbla_agebrith | 5       | NULL |    1 |   100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.01 sec)

也出现了Using filesort。
可以得出,order by也遵循最佳左前缀法则。
改变查询条件:

explain select * from tbla where birth > '2019-01-01 00:00:00' order by birth;

打印

+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key               | key_len | ref  | rows | filtered | Extra                                    |
+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+------------------------------------------+
|  1 | SIMPLE      | tbla  | NULL       | index | NULL          | idx_tbla_agebrith | 9       | NULL |    3 |    33.33 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.01 sec)

也出现了文件内排序,即是否出现文件内排序与where条件的字段无关,与order by后边的字段有关,order by关心的是后边接的用于排序的字段,而不是where条件句中的字段。

explain select * from tbla where birth > '2019-01-01 00:00:00' order by age desc,birth asc;

打印

+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key               | key_len | ref  | rows | filtered | Extra                                    |
+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+------------------------------------------+
|  1 | SIMPLE      | tbla  | NULL       | index | NULL          | idx_tbla_agebrith | 9       | NULL |    3 |    33.33 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.00 sec)

可得出,order by后边有多个字段时,如果排序方式不一致时也会出现文件内排序。

explain select * from tbla where birth > '2019-01-01 00:00:00' order by age desc,birth desc;

打印

+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key               | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | tbla  | NULL       | index | NULL          | idx_tbla_agebrith | 9       | NULL |    3 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

显然,order by后边有多个字段时,如果排序方式一致时不会出现文件内排序。

分析总结:

MySQL支持两种方式的排序,filesort和index。
index效率高,MySQL扫描索引本身完成排序;filesort方式效率较低。
order by在两种情况下,会使用index方式排序:

  • order by语句使用索引最左前列;
  • 使用where子句与order by子句条件组合满足索引最左前列。
explain select * from tbla where age > 30 order by birth;

打印

+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                                    |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
|  1 | SIMPLE      | tbla  | NULL       | range | idx_tbla_agebrith | idx_tbla_agebrith | 5       | NULL |    1 |   100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+-------------------+-------------------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.01 sec)

会出现文件内排序。

explain select * from tbla where age = 30 order by birth;

打印

+----+-------------+-------+------------+------+-------------------+-------------------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys     | key               | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+-------+------------+------+-------------------+-------------------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | tbla  | NULL       | ref  | idx_tbla_agebrith | idx_tbla_agebrith | 5       | const |    1 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+------+-------------------+-------------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

去掉范围后,age和birth可以组成最佳左前缀,无文件内排序。

5.filesort算法:

filesort有两种算法:双路排序和单路排序。

双路排序:

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

单路排序:

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

在数据量超过缓存区大小sort_buffer_size的时候,可能出现单路排序性能低于双路排序的情况,需要进行优化:

优化策略:

调整MySQL参数:

  • 增加sort_buffer_size参数设置;
  • 增大max_lenght_for_sort_data参数的设置。

6.提高order by的速度:

  • order by时select * 是一个大忌,只写需要的字段:
    当查询的字段大小总和小于max_length_for_sort_data而且排序字段不是text|blob类型时,会用改进后的算法–单路排序;
    两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次I/O。
  • 尝试提高sort_buffer_size。
  • 尝试提高max_length_for_sort_data。

group by的用法、分析、优化与order by类似。

7.练习

索引a_b_c(a,b,c)

SQL语句 是否会出现filesort
order by a,b 不会
order by a,b,c 不会
order by a desc,b desc,c desc 不会
where a = const order by b,c 不会
where a = const and b = const order by c 不会
where a = const and b > const order by b,c 不会
order by a asc,b desc,c desc
where g = const order by b,c
where a = const order by c
where a = const order by a,d

二、慢查询日志

1.定义

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

2.使用

默认情况下,MySQL数据库没有开启慢查询日志,需要我们手动来设置这个参数;
如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响,影响效率。
慢查询日志支持将日志记录写入文件。
查看慢查询日志状态:

show variables like '%slow_query_log%';

打印

+---------------------+----------------------------------------------------------------------------+
| Variable_name       | Value                                                                      |
+---------------------+----------------------------------------------------------------------------+
| slow_query_log      | OFF                                                                        |
| slow_query_log_file | E:\MySQL\phpstudy_pro\Extensions\MySQL5.7.26\data\xxx.log |
+---------------------+----------------------------------------------------------------------------+
2 rows in set, 1 warning (0.01 sec)

slow_query_log_file是slow_query_log的保存路径,可以在配置文件中修改。
slow_query_log默认关闭,可通过下面命令开启:

set global slow_query_log = 1;

打印

Query OK, 0 rows affected (0.01 sec)

再次查询

show variables like '%slow_query_log%';

打印

+---------------------+----------------------------------------------------------------------------+
| Variable_name       | Value                                                                      |
+---------------------+----------------------------------------------------------------------------+
| slow_query_log      | ON                                                                         |
| slow_query_log_file | E:\MySQL\phpstudy_pro\Extensions\MySQL5.7.26\data\xxx.log |
+---------------------+----------------------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

以上在命令行中的修改都是临时的,重启MySQL会恢复默认,要想永久修改,要在配置文件my.ini中修改,并重启服务才能生效。
查看响应时间阙值:

show variables like '%long_query_time%';

打印

+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set, 1 warning (0.01 sec)

可知,时间阙值默认为10秒。
重新设置阙值

set global long_query_time = 3;

打印

Query OK, 0 rows affected (0.00 sec)

此时再次查看阙值

show variables like '%long_query_time%';

打印

+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 3.000000 |
+-----------------+----------+
1 row in set, 1 warning (0.01 sec)

如果未改变,可以新打开一个命令行,连接数据库即可看到变为3秒。
此时进行测试

select sleep(4);

打印

+----------+
| sleep(4) |
+----------+
|        0 |
+----------+
1 row in set (4.00 sec)

sleep()函数可以实现阻塞功能,阻塞给定的时间,可以实现用于测试慢查询的时间。
此时可查看慢查询日志文件,可看到之前执行的慢SQL以及被记录到日志:

E:\MySQL\phpstudy_pro\COM\..\Extensions\MySQL5.7.26\\bin\mysqld.exe, Version: 5.7.26 (MySQL Community Server (GPL)). started with:
TCP Port: 3306, Named Pipe: MySQL
Time                 Id Command    Argument
# Time: 2019-12-19T11:22:04.811013Z
# User@Host: root[root] @ localhost [::1]  Id:    13
# Query_time: 4.000478  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
use demo;
SET timestamp=1576754524;
select sleep(4);

一般情况下不应该开启慢查询日志,在测试环境下可以使用,在生产环境下可以使用。

3.慢查询日志工具

mysqldumpslow,一般是在Linux系统下使用。

符号 意义
s 表示按照何种方式排序
c 访问次数
l 锁定时间
r 返回记录
t 查询时间
al 平均锁定时间
ar 平均返回记录数
t 即为返回前面多少条的数据

例如,
得到返回记录集最多的10个SQL:

mysqldumpslow -s r -t 10 D:/phpStudy/PHPTutorial/MySQL/slow_log.txt;

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

mysqldumpslow -s c -t 10 D:/phpStudy/PHPTutorial/MySQL/slow_log.txt;

查询有多少条慢SQL语句:

show global status like '%slow_queries%';

打印

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries  | 3     |
+---------------+-------+
1 row in set (0.01 sec)

易知,此时有3条慢SQL语句。

三、批量插入数据

1.创建表:

--部门表
create table dept(
            id int primary key auto_increment,
            deptno mediumint not null,
            dname varchar(20) not null,
            loc varchar(13) not null
)engine=innodb default charset=gbk;
--员工表
create table emp(
            id int primary key auto_increment,
            empno mediumint not null,   
            ename varchar(20) not null, 
            job varchar(9) not null, 
            mgr mediumint not null,  
            hiredate DATE not null,  
            sal decimal(7,2) not null, 
            comm decimal(7,2) not  null, 
            deptno mediumint not null   
)engine=innodb default charset=gbk;

打印

Query OK, 0 rows affected (0.03 sec)

Query OK, 0 rows affected (0.02 sec)

2.创建函数

$$ —— 定界符、分隔符。
DELIMITER $$:开始;
END $$:结束。
随机产生字符串:

--开始符
DELIMITER $$
--创建函数
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
--声明变量
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
--开始循环
WHILE i < n DO
--contact连接字符串
SET return_str = CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
--结束循环
END WHILE;
RETURN return_str;
--结束符
END $$

随机产生部门编号:

DELIMITER $$
CREATE FUNCTION rand_num() RETURNS INT(5)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(100+RAND()*10);
RETURN i;
END $$

创建函数,如果报错:this function has none of DETERMINISTIC... 时设置参数

set global log_bin_trust_function_creators=1;

3.创建存储过程

存储过程用于批量处理数据。
为emp创建存储过程、插入数据:

DELIMITER $$
CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
--关闭自动提交,最后一起提交
SET autocommit = 0;
REPEAT
SET i = i + 1;
--调用函数插入
INSERT INTO emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) VALUES ((START+i),rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
UNTIL i = max_num
END REPEAT;
--一起提交
COMMIT;
END $$

为dept创建存储过程、插入数据:

DELIMITER $$
CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO dept(deptno,dname,loc) VALUES ((START + i),rand_string(10),rand_string(8));
UNTIL i = max_num
END REPEAT;
COMMIT;
END $$

建议在可视化界面里执行函数和存储过程,因为可以多行同时输入并同时执行,不会产生语法错误;
如果在命令行里执行,要逐行输入,不可一次性复制粘贴,会出现语法错误,并且在DELIMITER $$语句后MYSQL的结束符变为"$$",要再次执行DELIMITER ;才会使结束符变为";",执行一般的SQL语句。

4.调用存储过程

调用存储过程实现批量插入数据:

delimiter ; 
call insert_dept(100,10);
    
call insert_emp(100001,500000);

打印

Query OK, 0 rows affected (0.02 sec)

Query OK, 0 rows affected (55.29 sec)

四、Show Profile进行SQL分析

1.定义

Show Profile是MySQL提供可以用来分析当前会话中语句执行的资源消耗情况,可以用于SQL的调优的测量。
默认情况下,参数处于关闭状态,并保存最近15次的运行结果。

2.Show Profile分析步骤:

  • 是否支持,看看当前MySQL版本是否支持;
  • 开启功能,默认是关闭,使用前需要开启。

版本 > 5.5均支持Show Profile。
查看状态:

show variables like 'profiling';

打印

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling     | OFF   |
+---------------+-------+
1 row in set, 1 warning (0.01 sec)

改变状态

--set profiling = on;
set profiling = 1;

打印

Query OK, 0 rows affected, 1 warning (0.00 sec)

再次查看状态:

show variables like 'profiling';

打印

+---------------+-------+         
| Variable_name | Value |         
+---------------+-------+         
| profiling     | ON    |         
+---------------+-------+         
1 row in set, 1 warning (0.00 sec)

此时已经打开。
进行几次查询:

select * from students group by age limit 3; 

打印

+----+-------+------+--------+--------+------------+-----------+--------+
| id | NAME  | age  | gender | cls_id | birth      | is_delete | height |
+----+-------+------+--------+--------+------------+-----------+--------+
|  6 | John  |   16 | secret |      3 | 1999-05-09 |         1 | 165.00 |
|  3 | Nancy |   17 | male   |      2 | 1999-08-09 |         1 | 165.00 |
|  1 | Tom   |   18 | male   |      1 | 1999-09-09 |         1 | 165.00 |
+----+-------+------+--------+--------+------------+-----------+--------+
3 rows in set (0.01 sec)
select * from students order by age limit 3; 

打印

+----+-------+------+--------+--------+------------+-----------+--------+
| id | NAME  | age  | gender | cls_id | birth      | is_delete | height |
+----+-------+------+--------+--------+------------+-----------+--------+
| 25 | John  |   16 | secret |      1 | 1999-05-09 |         1 | 164.00 |
|  6 | John  |   16 | secret |      3 | 1999-05-09 |         1 | 165.00 |
|  3 | Nancy |   17 | male   |      2 | 1999-08-09 |         1 | 165.00 |
+----+-------+------+--------+--------+------------+-----------+--------+
3 rows in set (0.00 sec)
select * from students; 

打印

+----+-------+------+--------+--------+------------+-----------+--------+ 
| id | NAME  | age  | gender | cls_id | birth      | is_delete | height | 
+----+-------+------+--------+--------+------------+-----------+--------+ 
|  1 | Tom   |   18 | male   |      1 | 1999-09-09 |         1 | 165.00 | 
|  2 | Jerry |   19 | male   |      3 | 1999-10-09 |         1 | 157.00 | 
|  3 | Nancy |   17 | male   |      2 | 1999-08-09 |         1 | 165.00 | 
|  4 | John  |   19 | secret |      1 | 1999-07-09 |         1 | 168.00 | 
|  6 | John  |   16 | secret |      3 | 1999-05-09 |         1 | 165.00 | 
|  7 | John  |   19 | secret |      1 | 1999-07-09 |         1 | 176.00 | 
|  8 | John  |   19 | secret |      2 | 1999-07-09 |         1 | 165.00 | 
|  9 | John  |   19 | secret |      3 | 1999-07-09 |         1 | 153.00 | 
| 10 | John  |   19 | secret |      3 | 1999-07-09 |         1 | 165.00 | 
| 11 | Rose  |   19 | female |      1 | 1999-07-09 |         1 | 169.00 | 
... 
+----+-------+------+--------+--------+------------+-----------+--------+ 
27 rows in set (0.00 sec)                                                 
select * from emp group by id%10 limit 15000; 

打印

+----+--------+--------+----------+-----+------------+---------+--------+--------+
| id | empno  | ename  | job      | mgr | hiredate   | sal     | comm   | deptno |
+----+--------+--------+----------+-----+------------+---------+--------+--------+
| 10 | 100011 | fIaaad | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    102 |
|  1 | 100002 | APwPJb | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    100 |
|  2 | 100003 | uKqxof | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    106 |
|  3 | 100004 | fxYAIL | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    106 |
|  4 | 100005 | jUWYeC | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    104 |
|  5 | 100006 | wUkqYU | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    105 |
|  6 | 100007 | hZDRyS | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    108 |
|  7 | 100008 | SJBNmU | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    107 |
|  8 | 100009 | ccemXd | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    104 |
|  9 | 100010 | nQrmha | SALESMAN |   1 | 2019-12-18 | 2000.00 | 400.00 |    106 |
+----+--------+--------+----------+-----+------------+---------+--------+--------+
10 rows in set (1.64 sec)                                                         

查看各个SQL语句的执行时间:

show profiles;

打印

+----------+------------+----------------------------------------------+
| Query_ID | Duration   | Query                                        |
+----------+------------+----------------------------------------------+
|        1 | 0.00027575 | select * from students group by age limit 3  |
|        2 | 0.00029700 | select * from students order by age limit 3  |
|        3 | 0.00021350 | select * from students                       |
|        4 | 1.65845325 | select * from emp group by id%10 limit 15000 |
+----------+------------+----------------------------------------------+
4 rows in set, 1 warning (0.00 sec)

查看单个SQL语句的执行情况

show profile cpu,block io for query 1;

打印

+----------------------+----------+----------+------------+--------------+---------------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting             | 0.000057 | 0.000000 |   0.000000 |         NULL |          NULL |
| checking permissions | 0.000009 | 0.000000 |   0.000000 |         NULL |          NULL |
| Opening tables       | 0.000017 | 0.000000 |   0.000000 |         NULL |          NULL |
| init                 | 0.000048 | 0.000000 |   0.000000 |         NULL |          NULL |
| System lock          | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
| optimizing           | 0.000002 | 0.000000 |   0.000000 |         NULL |          NULL |
| optimizing           | 0.000002 | 0.000000 |   0.000000 |         NULL |          NULL |
| statistics           | 0.000013 | 0.000000 |   0.000000 |         NULL |          NULL |
| preparing            | 0.000010 | 0.000000 |   0.000000 |         NULL |          NULL |
| statistics           | 0.000020 | 0.000000 |   0.000000 |         NULL |          NULL |
| preparing            | 0.000009 | 0.000000 |   0.000000 |         NULL |          NULL |
| executing            | 0.000012 | 0.000000 |   0.000000 |         NULL |          NULL |
| Sending data         | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
| executing            | 0.000002 | 0.000000 |   0.000000 |         NULL |          NULL |
| Sending data         | 0.001247 | 0.000000 |   0.000000 |         NULL |          NULL |
| end                  | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
| query end            | 0.000006 | 0.000000 |   0.000000 |         NULL |          NULL |
| closing tables       | 0.000001 | 0.000000 |   0.000000 |         NULL |          NULL |
| removing tmp table   | 0.000156 | 0.000000 |   0.000000 |         NULL |          NULL |
| closing tables       | 0.000006 | 0.000000 |   0.000000 |         NULL |          NULL |
| freeing items        | 0.000038 | 0.000000 |   0.000000 |         NULL |          NULL |
| cleaning up          | 0.000015 | 0.000000 |   0.000000 |         NULL |          NULL |
+----------------------+----------+----------+------------+--------------+---------------+
22 rows in set, 1 warning (0.00 sec)

可以看出,在主要操作之外,删除临时表removing tmp table用时相对较长。
进行验证:

explain select * from students group by age limit 3;

打印

+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                           |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
|  1 | SIMPLE      | students | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   27 |   100.00 | Using temporary; Using filesort |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
1 row in set, 1 warning (0.01 sec)

出现Using temporary,确实用到了临时表,先创建临时表,将查询到的数据存到临时表,再从临时表查询数据,再删除临时表,所以会耗费很多时间。
再查看一个SQL语句

show profile cpu,block io for query 4;

打印

+----------------------+----------+----------+------------+--------------+---------------+
| Status               | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting             | 0.000067 | 0.000000 |   0.000000 |         NULL |          NULL |
| checking permissions | 0.000005 | 0.000000 |   0.000000 |         NULL |          NULL |
| Opening tables       | 0.002077 | 0.000000 |   0.000000 |         NULL |          NULL |
| init                 | 0.000030 | 0.000000 |   0.000000 |         NULL |          NULL |
| System lock          | 0.000011 | 0.000000 |   0.000000 |         NULL |          NULL |
| optimizing           | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
| statistics           | 0.000022 | 0.000000 |   0.000000 |         NULL |          NULL |
| preparing            | 0.000009 | 0.000000 |   0.000000 |         NULL |          NULL |
| Creating tmp table   | 0.000032 | 0.000000 |   0.000000 |         NULL |          NULL |
| Sorting result       | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
| executing            | 0.000001 | 0.000000 |   0.000000 |         NULL |          NULL |
| Sending data         | 1.659538 | 1.515625 |   0.062500 |         NULL |          NULL |
| Creating sort index  | 0.000060 | 0.000000 |   0.000000 |         NULL |          NULL |
| end                  | 0.000004 | 0.000000 |   0.000000 |         NULL |          NULL |
| query end            | 0.000007 | 0.000000 |   0.000000 |         NULL |          NULL |
| removing tmp table   | 0.000008 | 0.000000 |   0.000000 |         NULL |          NULL |
| query end            | 0.000003 | 0.000000 |   0.000000 |         NULL |          NULL |
| closing tables       | 0.000077 | 0.000000 |   0.000000 |         NULL |          NULL |
| freeing items        | 0.000061 | 0.000000 |   0.000000 |         NULL |          NULL |
| cleaning up          | 0.000015 | 0.000000 |   0.000000 |         NULL |          NULL |
+----------------------+----------+----------+------------+--------------+---------------+
20 rows in set, 1 warning (0.00 sec)                                                      

出现了Sorting result,说明存在文件内排序。
进行验证

explain select * from emp group by id%10 limit 15000;

打印

+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                           |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+---------------------------------+
|  1 | SIMPLE      | emp   | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL | 1395176 |   100.00 | Using temporary; Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+---------------------------------+
1 row in set, 1 warning (0.00 sec)

出现了Using filesort,说明确实存在文件内排序,需要进行优化。
type参数:

参数 意义
all 显示所有的开销信息
block io 显示块IO相关开销
cpu 显示CPU相关开销信息
ipc 显示发送和接收相关开销信息
memory 显示内存相关开销信息
page faults 显示页面错误相关开销信息

参数注意

参数 意义
converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬
Creating tmp table 创建临时表
Copying to tmp table on disk 把内存中临时表复制到磁盘,危险
locked

3.全局查询日志

把每一条SQL语句记录下来,量大,无必要。
开启命令:

set global general_log = 1;

打印

Query OK, 0 rows affected (0.01 sec)

将SQL语句写到表中:

set global log_output = 'TABLE';

打印

Query OK, 0 rows affected (0.00 sec)

所编写的SQL语句,会记录到MySQL库里的genral_log表。
执行一条SQL语句,如

select * from students;

再查看全局查询日志:

select * from mysql.general_log;

打印

+----------------------------+------------------------------+-----------+-----------+--------------+---------------------------------------------------------------+
| event_time                 | user_host                    | thread_id | server_id | command_type | argument                                                      |
+----------------------------+------------------------------+-----------+-----------+--------------+---------------------------------------------------------------+
| 2019-12-18 21:47:51.376592 | root[root] @ localhost [::1] |        10 |         1 | Query        | use `mysql`                                                   |
| 2019-12-18 21:47:52.914806 | root[root] @ localhost [::1] |        10 |         1 | Query        | show full tables from `mysql` where table_type = 'BASE TABLE' |
| 2019-12-18 21:48:02.936032 | root[root] @ localhost [::1] |        10 |         1 | Query        | describe `mysql`.`general_log`                                |
| 2019-12-18 21:48:02.937085 | root[root] @ localhost [::1] |        10 |         1 | Query        | show index from `mysql`.`general_log`                         |
| 2019-12-18 21:48:05.235971 | root[root] @ localhost [::1] |        10 |         1 | Query        | select * from `mysql`.`general_log` limit 0, 1000             |
...
| 2019-12-18 22:07:08.593162 | root[root] @ localhost [::1] |        19 |         1 | Query        | show profile                                                  |
| 2019-12-19 20:38:20.901277 | root[root] @ localhost [::1] |        24 |         1 | Query        | select * from demo                                            |
| 2019-12-19 20:38:37.884983 | root[root] @ localhost [::1] |        24 |         1 | Query        | select * from studens                                         |
| 2019-12-19 20:38:43.057866 | root[root] @ localhost [::1] |        24 |         1 | Query        | select * from students                                        |
| 2019-12-19 20:39:55.765410 | root[root] @ localhost [::1] |        24 |         1 | Query        | select * from mysql.general_log                               |
+----------------------------+------------------------------+-----------+-----------+--------------+---------------------------------------------------------------+
28 rows in set (0.00 sec)

可知,刚刚执行的查询语句已经被保存到general_log。
小编那么拼,赞一个再撤!
欢迎大家加入群聊【python知识交流分享群】,进行技术交流:https://jq.qq.com/?_wv=1027&k=5m5AduZ
在这里插入图片描述
公众号二维码
也可以关注我的公众号:Python极客社区,在我的公众号里,经常会分享很多Python的文章,而且也分享了很多工具、学习资源等,另外回复“电子书”还可以获取十本我精心收集的Python电子书。

发布了51 篇原创文章 · 获赞 184 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/CUFEECR/article/details/103622956
今日推荐