SELECT … FROM table WHERE EXISTS(subquery)
该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(TRUE或 FALSE)来决定查询的数据结果是否得以保留。
1 EXISTS (subquery)只返回TRUE或FALSE, 因此子查询中的SELECT *
也可以是SELECT 1
或 select 'X'
, 官方说法是实际执行时会忽略SELECT清单,因此没有区别。
2 EXISTS子查询的实际执行过程可能经过了优化而不是我们理解上的逐条对比,如果担忧效率问题,可进行实际检验以确定是否有效率问题。
3 EXISTS子查询往往也可以用条件表达式、其他子查询或者JOIN来替代,何种最优需要具体问题具体分析。
1.ORDER BY子句,尽量使用 Index 方式排序,避免使用 FileSort 方式排序
建表SQL
CREATE TABLE tblA(
#id primary key not nuull auto_increment,
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());
CREATE INDEX idx_A_ageBirth ON tblA(age,birth);
select * from tblA;
MySQL支持二种方式的排序,FileSort 和 Index, Index效率高。
它指MySQL扫描索引本身完成排序。FileSort方式效率较低。
ORDER BY满足两情况,会使用 Index 方式排序:
尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀。
2.如果不在索引列上,filesort有两种算法:mysq|就要启动双路排序和单路排序
取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法,就是单路排序。
结论及引申出的问题:
3.优化策略
提高Order By的速度
1.Order by 时 select * 是一个大忌只 Query 需要的字段,这点非常重要。 在这里的影响是:
1.1 当Query的字段大小总和小于 max_length_for_sort_data 而且排序字段不是 TEXT | BLOB 类型时,会用改进后的算法 ----- 单路排序,否则用老算法 ---- 多路排序。
1.2 两种算法的数据都有可能超出sort_buffer的容量, 超出之后,会创建tmp文件进行合并排序,导致多次I/O, 但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size。
2.尝试提高sort_buffer_size
不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的。
3.尝试提高max_length_for_ sort_data
提高这个参数,会增加用 改进算法的概率。但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率。
默认情况下,MySQL数据库没有开启慢查询日志,需要我们手动来设置这个参数。
当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件
默认情况下 slow_query_log 的值为OFF,表示慢查询日志是禁用的,可以通过设置 slow_query_log 的值来开启。
默认:SHOW VARIABLES LIKE ‘%slow_query_log%’;
开启:set global slow_query_log=1;
开启慢查询日志只对当前数据库生效
,如MySQL重启后则会失效。
如果要永久生效,就必须修改配置文件my.cnf (其他系统变量也是如此)
修改my.cnf 文件,[mysqld]下增加或修改参数
slow_query_log 和 slow_query_log_file 后,然后重启MySQL服务器。也即将如下两行配置进my.cnf 文件
slow_query_log=1
slow_query_log_file=/var/lib/mysql/atguigu-slow.log
关于慢查询的参数slow_query_log_file, 它指定慢查询日志文件的存放路径,系统默认会给一个缺省的文件host_name-slow.log(如果没有指定参数slow_query_log_file的话)
这个是由参数long_query_time 控制,默认情况下long_query_tome的值为10秒,
命令:SHOW VARIABLES LIKE ‘%long_query_time%’;
可以使用命令修改,也可以在my.cnf 参数里面修改。
加入运行时间正好等于long_query_time的情况,并不会被记录下来。也就是说,在mysql源码里是判断大于long_query_time,而非大于等于。
查看当前多少秒算慢:SHOW VARIABLES LIKE ‘%long_query_time%’;
设置慢的阙值时间:使用 set global slow_query_log=3; 修改为阙值到3秒钟的就是慢sql
为什么设置后看不出变化?
记录慢SQL并后续分析
查询当前系统中有多少条慢查询记录
show global status like ‘%Slow_queries%’;
【mysqld】下配置:
slow_query_log=1
slow_query_log_file=/var/lib/mysql/atguigu-slow.log
long_query_time=3;
log_output=FILE
在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow。
查看mysqldumpslow的帮助信息:
命令:mysqldumpslow --help
工作常用参数:
得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/ib/mysql/atguigu-slow.log
得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 Nvar/lib/mysql/atguigu-slow.log
得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g “left join” /var/lib/mysql/atguigu-slow.log
另外建议在使用这些命令时结合 | 和 more 使用,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/ib/mysql/atguigu-slow.log | more
往表里插入1000W数据
# 新建表
create database bigData;
use bigData;
#1 建表dept
CREATE TABLE dept(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
dname VARCHAR(20) NOT NULL DEFAULT "",
loc VARCHAR(13) NOT NULL DEFAULT ""
)ENGINE=INNODB DEFAULT CHARSET=GBK;
#2 建表emp
CREATE TABLE emp(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
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=INNODB DEFAULT CHARSET=GBK;
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(100) 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 $$
#假如要删除
#drop funtion rand_string();
DELIMITER $$
CREATE FUNCTION rand_num()
RETURNS INT(5)
BEGIN
DECLARE i INT DEFAULT 0;
SET i=FLOOR(100+RAND()*10);
RETURN i;
END $$
#假如要删除
#drop funtion rand_num();
DELIMITER $$
CREATE FUNCTION 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(empno,ename,job,mgr,hirdate,sal,comm,deptno) VALUES((START+i),
rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
UNTIL i = max_num
END REPEAT;
COMMIT;
END $$
DELIMITER $$
CREATE FUNCTION 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;
CALL insert_dept(100,10);
DELIMITER;
CALL insert_emp(100001,500000);
是mysq|提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量。
http://dev.mysql.com/doc/refman/5.5/en/show-profile.html
默认情况下,参数处于关闭状态,并保存最近15次的运行结果。
1.是否支持,看看当前的mysq|版本是否支持
命令:Show variables like ‘profiling’;
默认是关闭,使用前需要开启 。
或者:Show variables like ‘profiling%’;
2.开启功能,默认是关闭,使用前需要开启
4.查看结果,show profiles;
5.诊断SQL, show profile cpu,block io for query上一步前面的问题SQL数字号码
6.日常开发需要注意的结论
在mysql的 my.cnf 中,设置如下:
#开启
general_log=1
#记录日志文件的路径
general_log_file=/path/logfile
#输出格式
log_output=FILE
此后,你所编写的sql语句,将会记录到mysql库里的general_log表,可以用下面的命令查看。
永远不要再生产环境开启这个功能。