目录
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情况
- 2.开启慢查询日志,设置阙值,比如超过5秒钟的就是慢SQL,并将它抓取出来
- 3.explain+慢查询SQL分析
- 4.show profile查询SQL在mysql服务器里面的执行细节和生命周期情况
- 5.SQL数据库服务器的参数调优
这里类似于嵌套循环,如下
//第一种情况
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来替代,何种最优需要具体问题具体分析
建表
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());
建立索引
接下来的关注点为会不会产生filesort
1)
order by后排序的字段顺序是按照索引建立的顺序进行,无filesort
2)
order by后排序的字段顺序是按照索引建立的顺序进行,无filesort
3)
order by后排序的字段顺序不是按照索引建立的顺序进行,跳过了带头大哥age,Using filesort
4)
order by后排序的字段顺序与索引建立的顺序相反,Using filesort
5)
order by后排序的字段顺序不是按照索引建立的顺序进行,跳过了带头大哥age,Using filesort
6)
order by后排序的字段顺序不是按照索引建立的顺序进行,跳过了带头大哥age,Using filesort
7)
order by后排序的字段顺序是按照索引建立的顺序进行,无filesort
8)
建好索引的排列顺序默认按照升序排列,而上图order by后的birth字段要求降序排列,建好的索引用不上,mysql只能进行文件内排序
总结:mysql支持两种方式的排序:FileSort和Index,Index效率高,即mysql扫描索引本身完成排序,FileSort效率较低
order by满足以下两种情况,会使用Index方式排序:
尽可能在索引列上完成排序操作,遵循索引创建的最左前缀
如果不在索引列上,filesort有两种算法,mysql就要启动双路排序和单路排序
双路排序:
- mysql4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据
- 读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出
- 从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段
取一批数据,要对磁盘进行两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法:
单路排序:
从磁盘读取查询需要的所有列,按照order by列在buffer对他们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据,并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中
由于单路排序是后出的,总体而言好过双路,但是单路有以下问题:
优化策略:
提高order by的速度:
1.order by时select * 是一个大忌只查询需要的字段,这点非常重要,在这里的影响是:
2.尝试提高sort_buffer_size
不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程
3.尝试提高max_length_for_sort_data
提高这个参数,会增加用改进算法的概率,但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率
示例:
index a_b_c(a,b,c)
——order by a
——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 order by b,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 //丢失a索引
——where a=const order by c //丢失b索引
——where a=const order by a,d //d不是索引的一部分
——where a in (...) order by b,c //对于排序来说,多个相等条件也是范围查询
group by实质是先排序后进行分组,遵循索引建的最佳左前缀
当无法使用索引列,增大max_length_for_sort_data参数的设置,增加sort_buffer_size参数的设置
group by的优化几乎和上述的order by一致,不过,group by多了如下:
where高于having,能写在where限定的条件就不要去having限定了
默认,mysql数据库没有开启慢查询日志,需要我们手动来设置这个参数
当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能的影响,慢查询日志支持将日志记录写入文件
//查看是否开启
SHOW VARIABLES LIKE '%slow_query_log%';
//下列方法只对当前数据库有效,数据库重启后失效
set GLOBAL slow_query_log=1;
以上方法只对当前数据库生效,数据库重启后失效
如果要永久生效,就必须修改配置文件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服务器
这个是由参数long_query_time控制,默认情况下long_query_time的值为10秒
命令:
show variables like 'long_query_time';
同样可以使用命令修改,也可以在my.cnf配置文件中的参数里面设置
假如运行时间正好等于long_query_time的情况,并不会被记录下来,也就是说,在mysql的源码里是判断大于long_query_time,而非大于等于
设置慢的阙值时间为3秒钟
为什么修改之后再次查看long_query_time后无变化?
方式1:需要重新连接或新开一个会话show variables like 'long_query_time';才能看到变化
方式2:在原会话中使用show global variables like 'long_query_time';
模拟一个执行4秒的慢SQL
查询上述我们设置的慢日志文件
可以看到慢的SQL语句被记录在日志中,并且在日志中还有关于它的详细信息
查询当前系统中有多少条慢查询记录
在生产环境中,要手工分析日志,查找、分析SQL,显然是个体力活,mysql提供了日志分析工具 mysqldumpslow
查询mysqldumpslow的帮助信息
使用参考示例:
1.得到返回记录集最多的10个SQL
2.得到访问次数最多的10个SQL
3.得到按照时间排序的前10条里面含有左连接的查询语句
4.另外建议在使用这些命令时结合|和more使用,否则有可能出现爆屏情况