MySQL数据库优化技术之SQL语句慢查询定位

时间:2022-08-04 18:48:58

通过show status命令了解各种SQL的执行频率

MySQL客户端连接成功后,通过使用show [session|global] status 命令可以提供服务器状态信息;

其中的session来表示当前的连接的统计结果,global来表示自数据库启动至今的统计结果,默认是session级别的。

show status 常用命令:

show status like 'com_%';

其中com_xxx表示xxx语句所执行的次数;

重点注意com_select, com_insert, com_update, com_delete通过这几个参数,可以容易的了解到当前数据库应用是以插入更新为主还是以查询为主(来决定不同的存储引擎),以及各类的SQL大致执行比例是多少。

show status like 'com_insert';
show status like 'com_delete';
show status like 'com_update';
show status like 'com_select';
show status like 'connections'; //连接到MySQL服务器的连接数
show status like 'uptime'; //服务器的工作时间(单位:s)
show status like 'slow_queries'; //慢查询的次数(默认是慢查询时间:10S):不仅是查询,insert也包括的

如何定位慢查询(执行效率低)的SQL语句

默认情况下,MySQL认为10秒才是一个慢查询。

mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

set long_query_time=1; //修改mysql慢查询时间

mysql> set long_query_time=1;
Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+

注意:set只对本MySQL Client有效。当打开一个新的客户端的时候,还是默认值10秒。

也可以在my.ini中进行配置,就会全局有效:

long_query_time=1

构建一个大表(400W)用于测试mysql优化语句:

#创建表DEPT
CREATE TABLE dept( /*部门表*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
dname VARCHAR(20) NOT NULL DEFAULT "",
loc VARCHAR(13) NOT NULL DEFAULT ""
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ; #创建表EMP雇员
CREATE TABLE emp
(empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
hiredate DATE NOT NULL,/*入职时间*/
sal DECIMAL(7,2) NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*红利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
)ENGINE=MyISAM DEFAULT CHARSET=utf8 ; #工资级别表
CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
losal DECIMAL(17,2) NOT NULL,
hisal DECIMAL(17,2) NOT NULL
)ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999); # 随机产生字符串
#定义一个新的命令结束符合
delimiter $$
#删除自定的函数
drop function rand_string $$ #这里创建了一个函数.
#rand_string(n INT) rand_string 是函数名 (n INT) //该函数接收一个整数
create function rand_string(n INT)
returns varchar(255) #该函数会返回一个字符串
begin
#chars_str定义一个变量 chars_str,类型是 varchar(100),默认值'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare chars_str varchar(100) default
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare return_str varchar(255) default '';
declare i int default 0;
while i < n do
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 ;
select rand_string(6); # 随机产生部门编号
delimiter $$
drop function rand_num $$ #这里我们又自定了一个函数
create function rand_num( )
returns int(5)
begin
declare i int default 0;
set i = floor(10+rand()*500);
return i;
end $$ delimiter ;
select rand_num(); #******************************************
#向emp表中插入记录(海量的数据) delimiter $$
drop procedure insert_emp $$ #随即添加雇员[光标] 400w
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
#set autocommit =0 把autocommit设置成0
set autocommit = 0;
repeat
set i = i + 1;
insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
until i = max_num
end repeat;
commit;
end $$ delimiter ;
#调用刚刚写好的函数, 1800000条记录,从100001号开始
call insert_emp(100001,4000000); select count(*) from emp;
#4000000 #**************************************************************
# 向dept表中插入记录 delimiter $$
drop procedure insert_dept $$ 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 values ((start+i) ,rand_string(10),rand_string(8));
until i = max_num
end repeat;
commit;
end $$ delimiter ;
call insert_dept(100,10); #------------------------------------------------
#向salgrade 表插入数据
delimiter $$
drop procedure insert_salgrade $$
create procedure insert_salgrade(in start int(10),in max_num int(10))
begin
declare i int default 0;
set autocommit = 0;
ALTER TABLE emp DISABLE KEYS;
repeat
set i = i + 1;
insert into salgrade values ((start+i) ,(start+i),(start+i));
until i = max_num
end repeat;
commit;
end $$
delimiter ;
#测试不需要了
#call insert_salgrade(10000,1000000);
#----------------------------------------------

事先将long_query_time设置为1

mysql> select * from emp where empno=234567;
+--------+--------+----------+-----+------------+---------+--------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+--------+----------+-----+------------+---------+--------+--------+
| 234567 | fgqmkp | SALESMAN | 1 | 2014-09-23 | 2000.00 | 400.00 | 444 |
+--------+--------+----------+-----+------------+---------+--------+--------+
1 row in set (8.09 sec)

总共花费了8.09秒

mysql> show status like 'slow_queries';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries | 2 |
+---------------+-------+
1 row in set (0.00 sec)

为什么是2???其实统计的并不只是select,前面通过存储过程插入数据也算。

将慢查询的sql记录到我们的一个日志中

在默认情况下mysql不记录慢查询日志,需要在启动的时候指定

bin\mysqld.exe --safe-mode  --slow-query-log [mysql5.5 可以在my.ini指定]

bin\mysqld.exe --log-slow-queries=d:/abc.log [低版本mysql5.0可以在my.ini指定]

关闭mysql服务并启动:bin\mysqld.exe --safe-mode  --slow-query-log

如果启用了慢查询日志,默认把这个文件放在my.ini 文件中记录的位置: datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"

下创建了一个:hostname-slow.log的文件

由于重启了mysql服务,为了测试,则需要重新设置慢查询时间:set long_query_time=1;

mysql> select * from emp where empno=234567;
+--------+--------+----------+-----+------------+---------+--------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+--------+----------+-----+------------+---------+--------+--------+
| 234567 | fgqmkp | SALESMAN | 1 | 2014-09-23 | 2000.00 | 400.00 | 444 |
+--------+--------+----------+-----+------------+---------+--------+--------+
1 row in set (8.01 sec)

那么在hostname-show.log文件中就记录下了如下慢查询信息:

# Time: 140923 22:06:13
# User@Host: root[root] @ localhost [127.0.0.1]
# Query_time: 8.011458 Lock_time: 0.037002 Rows_sent: 1 Rows_examined: 4000000
use mysqltest;
SET timestamp=1411481173;
select * from emp where empno=234567;

当第二次执行相同的sql语句时,速度会快很多。