MySQL高级---第二部分【索引优化】

---分析---
1.观察一天,看看生产的慢SQL情况;
2.开启慢查询日志,设置阈值,必读超过5秒就是 慢SQL,并将它抓取出来;
3.ecplain+慢SQL分析
4.show profile
5.运维经理or DBA,进行SQL数据库服务器的参数调优


==总结
1.慢查询的开启并捕获
2.explain+慢SQL分析
3.show profile查询SQL在MYSQL服务器里面的执行细节和生命周期
4.SQL数据库服务器的参数调优。

3.5 索引优化

3.5.1 单表查询优化

1.建表

MySQL高级---第二部分【索引优化】_第1张图片

2. 实例

#查询 category_id 为1 且  comments 大于 1 的情况下,views 最多的 article_id。 

#结论:很显然,type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。
#开始优化:
# 1.1 新建索引+删除索引

create index idx_article_ccv on article(category_id,comments,views);

# 1.2 第2次EXPLAIN

#结论:
#type 变成了 range,这是可以忍受的。但是 extra 里使用 Using filesort 仍是无法接受的。
#但是我们已经建立了索引,为啥没用呢?
#这是因为按照 BTree 索引的工作原理,
# 先排序 category_id,
# 如果遇到相同的 category_id 则再排序 comments,如果遇到相同的 comments 则再排序 views。
#当 comments 字段在联合索引里处于中间位置时,
#因comments > 1 条件是一个范围值(所谓 range),
#MySQL 无法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。

# 1.3 删除第一次建立的索引
DROP INDEX idx_article_ccv ON article; 
# 1.4 第2次新建索引
#ALTER TABLE `article` ADD INDEX idx_article_cv ( `category_id` , `views` ) ;
create index idx_article_cv on article(category_id,views);

# 1.5 第3次EXPLAIN

#结论:可以看到,type 变为了 ref,Extra 中的 Using filesort 也消失了,结果非常理想。
DROP INDEX idx_article_cv ON article;

3.5.2 双表查询优化

1.建表

MySQL高级---第二部分【索引优化】_第2张图片

2.案例:

# 下面开始explain分析

#结论:type 有All
# 添加索引优化
ALTER TABLE `book` ADD INDEX Y ( `card`);
# 第2次explain
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;

#可以看到第二行的 type 变为了 ref,rows 也变成了优化比较明显。
#这是由左连接特性决定的。LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有,
#所以右边是我们的关键点,一定需要建立索引。

建立一个右表索引
# 删除旧索引 + 新建 + 第3次explain
DROP INDEX Y ON book;
ALTER TABLE class ADD INDEX X (card);

建议:

1、保证被驱动表的join字段已经被索引。被驱动表  join 后的表为被驱动表  (需要被查询)

2、left join 时,选择小表作为驱动表,大表作为被驱动表。但是 left join 时一定是左边是驱动表,右边是被驱动表

3、inner join 时,mysql会自己帮你把小结果集的表选为驱动表。mysql 自动选择。小表作为驱动表。因为 驱动表无论如何都会被全表扫描?。所以扫描次数越少越好

4、子查询尽量不要放在被驱动表,有可能使用不到索引。若必须用到子查询,可将子查询设置为驱动表,,因为驱动表的type 肯定是 all,而子查询返回的结果表没有索引,必定也是all。

5、 左连接建在右表,右连接建在左表。

6.、索引最好设置早要经常查询的字段中

7.优先优化内层循环

3.5.3 案例(索引失效)

若一个字段上有多种索引呢?某一索引失效,可以继续使用其他索引不影响。

建表

MySQL高级---第二部分【索引优化】_第3张图片

1. 全值匹配我最爱

索引  idx_staffs_nameAgePos 建立索引时 以 name , age ,pos 的顺序建立的。全值匹配表示 按顺序匹配的

MySQL高级---第二部分【索引优化】_第4张图片

2.最佳左前缀法则

如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。

 and 忽略左右关系。既即使没有没有按顺序 由于优化器的存在,会自动优化。
经过试验结论  建立了 idx_nameAge 索引  id 为主键
    1.当使用覆盖索引的方式时,(select name/age/id from staffs where age=10 (后面没有其他没有索引的字段条件)),即使不是以 name 开头,也会使用 idx_nameAge 索引。
    既 select 后的字段 有索引,where 后的字段也有索引,则无关执行顺序。
    2.除开上述条件 才满足最左前缀法则。

MySQL高级---第二部分【索引优化】_第5张图片

3、不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

MySQL高级---第二部分【索引优化】_第6张图片

4.、存储引擎不能使用索引中范围条件右边的列

 范围 若有索引则能使用到索引,范围条件右边的索引会失效(范围条件右边与范围条件使用的同一个组合索引,右边的才会失效。若是不同索引则不会失效)

MySQL高级---第二部分【索引优化】_第7张图片

5、尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *

MySQL高级---第二部分【索引优化】_第8张图片

6、mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描

索引  idx_nameAgeJob
         idx_name
使用 != 和 <> 的字段索引失效( != 针对数值类型。 <> 针对字符类型
前提 where and 后的字段在混合索引中的位置比比当前字段靠后  where age != 10 and name='xxx'  ,这种情况下,mysql自动优化,将 name='xxx' 放在 age !=10 之前,name 依然能使用索引。只是 age 的索引失效)

MySQL高级---第二部分【索引优化】_第9张图片

7、is not null 也无法使用索引,但是is null是可以使用索引的

MySQL高级---第二部分【索引优化】_第10张图片

8、like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作

like ‘%abc%’  type 类型会变成 all
like ‘abc%’ type 类型为 range ,算是范围,可以使用索引

MySQL高级---第二部分【索引优化】_第11张图片

问题:解决like '%字符串%'时索引不被使用的方法??

使用覆盖索引解决这个问题

9、字符串不加单引号索引失效

 底层进行转换使索引失效,使用了函数造成索引失效

MySQL高级---第二部分【索引优化】_第12张图片

10、少用or,用它来连接时会索引失效

MySQL高级---第二部分【索引优化】_第13张图片

11、小总结

  假设index(a,b,c)

               Where语句        索引是否被使用
where a = 3 Y,使用到a
where a = 3 and b = 5  Y,使用到a,b
where a = 3 and b = 5 and c = 4 Y,使用到a,b,c
where b = 3 或者 where b = 3 and c = 4  或者 where c = 4 N,用不到索引
where a = 3 and c = 5 使用到a, 但是c不可以,b中间断了
where a = 3 and b > 4 and c = 5 使用到a和b, c不能用在范围之后,b后断了
where a = 3 and b like 'kk%' and c = 4 Y,使用到a,b, c
where a = 3 and b like '%kk' and c = 4 Y,只用到a
where a = 3 and b like '%kk%' and c = 4 Y,只用到a
where a = 3 and b like 'k%kk%' and c = 4   Y,使用到a,b,c

like就看第一个匹配的是不是常量 
MySQL高级---第二部分【索引优化】_第14张图片

3.5.4 面试题讲解

建表

MySQL高级---第二部分【索引优化】_第15张图片

【建索引】
create index idx_test03_c1234 on test03(c1,c2,c3,c4);

问题:我们创建了复合索引idx_test03_c1234 ,根据以下SQL分析下索引使用情况?

1、

2、

3、用到3个索引

4、  用到4个索引

5、c3作用在排序而不是查找

6、c3作用在排序而不是查找

7、出现了filesort

8.1、 只用c1一个字段索引,但是c2、c3用于排序,无filesort

8.2 出现了filesort,我们建的索引是1234,它没有按照顺序来,3 2 颠倒了

9、用c1、c2两个字段索引,但是c2、c3用于排序,无filesort

10、用c1、c2两个字段索引,但是c2、c3用于排序,无filesort

 本例有常量c2的情况,排序字段已经是一个常量,和8.2对比,无filesort

  有 filesort

11、 用到了一个索引

12. 分组之前必排序,grop by 和odder by几乎一致

3.5.5、一般性建议

对于单键索引,尽量选择针对当前query过滤性更好的索引

在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。(避免索引过滤性好的索引失效)

在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引

尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的

3.5.6 、去重优化

尽量不要使用 distinct 关键字去重:优化

例子:select kcdz form t_mall_sku where id in( 3,4,5,6,8 )  将产生重复数据,
          select distinct kcdz form t_mall_sku where id in( 3,4,5,6,8 )   使用 distinct 关键字去重消耗性能
优化: select  kcdz form t_mall_sku where id in( 3,4,5,6,8 )  group by kcdz 能够利用到索引

4. 查询截取分析

4.1 查询优化

4.1.1 永远小表驱动大表,类似嵌套循环

子查询优化,用in 还是 exists

MySQL高级---第二部分【索引优化】_第16张图片

MySQL高级---第二部分【索引优化】_第17张图片

有索引的情况下 用  inner join 是最好的  其次是 in  ,exists最糟糕
 
无索引的情况下用 
小表驱动大表 因为join 方式需要distinct ,没有索引distinct消耗性能较大 
所以  exists性能最佳 in其次  join性能最差?
 
无索引的情况下大表驱动小表
in 和 exists 的性能应该是接近的  都比较糟糕  exists稍微好一点 超不过5%     但是inner join 优于使用了 join buffer 所以快很多
如果left join 则最慢

4.1.2 order by关键字优化

4.1.2.1 ORDER  BY子句,尽量使用Index方式排序,避免使用FileSort方式排序

建表

MySQL高级---第二部分【索引优化】_第18张图片

建立索引:CREATE INDEX idx_A_ageBirth ON tblA(age,birth,name);

MySQL高级---第二部分【索引优化】_第19张图片

MySQL高级---第二部分【索引优化】_第20张图片

MySQL支持二种方式的排序,FileSort和Index,Index效率高。它指MySQL扫描索引本身完成排序。FileSort方式效率较低。

ORDER BY满足两情况,会使用Index方式排序:

ORDER BY 语句使用索引最左前列

使用Where子句与Order BY子句条件列组合满足索引最左前列

where子句中如果出现索引的范围查询(即explain中出现range)会导致order by 索引失效。

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

双路排序:

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

多路排序需要借助 磁盘来进行排序。所以 取数据,排好了取数据。两次 io操作。比较慢
单路排序 ,将排好的数据存在内存中,省去了一次 io 操作,所以比较快,但是需要内存空间足够。

从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。

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

单路排序:

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

结论及引申出的问题:

由于单路是后出的,总体而言好过双路

但是用单路有问题:

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

增大sort_buffer_size参数的设置,用于单路排序的内存大小

增大max_length_for_sort_data参数的设置,单次排序字段大小。(单次排序请求)

去掉select 后面不需要的字段,select 后的多了,排序的时候也会带着一起,很占内存,所以去掉没有用的

提高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高级---第二部分【索引优化】_第21张图片

第二种中,where a = const and b > const order by b , c 不会出现 using filesort  b , c 两个衔接上了
但是:where a = const and b > const order by  c 将会出现 using filesort 。因为 b 用了范围索引,断了。而上一个  order by 后的b 用到了索引,所以能衔接上 c 

4.1.3 GROUP BY关键字优化

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

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

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

5.2、慢查询日志

5.1是什么 

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

5.2 怎么玩

5.2.1 说明

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

5.2.2 查看是否开启及如何开启

默认:SHOW VARIABLES LIKE '%slow_query_log%';

开启:set global slow_query_log=1;

使用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的话)

5.2.3 那么开启了慢查询日志后,什么样的SQL才会记录到慢查询日志里面呢?

这个是由参数long_query_time控制,默认情况下long_query_time的值为10秒,
命令:SHOW VARIABLES LIKE 'long_query_time%';

MySQL高级---第二部分【索引优化】_第22张图片

可以使用命令修改,也可以在my.cnf参数里面修改。
 
假如运行时间正好等于long_query_time的情况,并不会被记录下来。也就是说,
在mysql源码里是判断大于long_query_time,而非大于等于

5.2.4 Case

1. 查看当前多少秒算慢

SHOW VARIABLES LIKE 'long_query_time%';

2.设置慢的阙值时间

使用命令 
set global long_query_time=1
修改为阙值到1秒钟的就是慢sql

MySQL高级---第二部分【索引优化】_第23张图片

修改后发现long_query_time并没有改变。

为什么设置后看不出变化?

  • 需要重新连接或新开一个会话才能看到修改值。 SHOW VARIABLES LIKE 'long_query_time%';
  • SHOW GLOBAL VARIABLES LIKE 'long_query_time%';

3.记录慢SQL并后续分析

 实验一条慢sql

MySQL高级---第二部分【索引优化】_第24张图片

跟踪日志信息

MySQL高级---第二部分【索引优化】_第25张图片

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

show global status like '%Slow_queries%';

MySQL高级---第二部分【索引优化】_第26张图片

5.2.5 配置版

【mysqld】下配置:
slow_query_log=1;
slow_query_log_file=/var/lib/mysql/atguigu-slow.log
long_query_time=3;
log_output=FILE

5.3 日志分析工具mysqldumpslow

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

5.3.1 查看mysqldumpslow的帮助信息

mysqldumpslow --help

MySQL高级---第二部分【索引优化】_第27张图片

MySQL高级---第二部分【索引优化】_第28张图片

5.3.2 工作常用参考

得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log
 
得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/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/lib/mysql/atguigu-slow.log | more

5.3 批量数据脚本

往表里插入1000W数据

1. 建表

# 新建库
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=UTF8 ;  
  
#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=UTF8 ; 

2.设置参数log_bin_trust_function_creators

创建函数,假如报错:This function has none of DETERMINISTIC......
# 由于开启过慢查询日志,因为我们开启了 bin-log, 我们就必须为我们的function指定一个参数。
 
show variables like 'log_bin_trust_function_creators';
 
set global log_bin_trust_function_creators=1;
 
# 这样添加了参数以后,如果mysqld重启,上述参数又会消失,永久方法:
 
windows下my.ini[mysqld]加上log_bin_trust_function_creators=1 
 
linux下    /etc/my.cnf下my.cnf[mysqld]加上log_bin_trust_function_creators=1

3.创建函数,保证每条数据都不同

创建函数,保证每条数据都不同

DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN    ##方法开始
 DECLARE chars_str VARCHAR(100) DEFAULT   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; 
 ##声明一个 字符窜长度为 100 的变量 chars_str ,默认值 
 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));
##concat 连接函数  ,substring(a,index,length) 从index处开始截取
 SET i = i + 1;
 END WHILE;
 RETURN return_str;
END $$
 
#假如要删除
#drop function 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 function rand_num;

 

4. 创建存储过程

创建往emp表中插入数据的存储过程

DELIMITER $$
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(empno, ename ,job ,mgr ,hiredate ,sal ,comm ,deptno ) VALUES ((START+i) ,rand_string(6),'SALESMAN',0001,CURDATE(),FLOOR(1+RAND()*20000),FLOOR(1+RAND()*1000),rand_num());  
 UNTIL i = max_num   ##直到  上面也是一个循环
 END REPEAT;  ##满足条件后结束循环
 COMMIT;   ##执行完成后一起提交
 END $$
 
#删除
# DELIMITER ;
# drop PROCEDURE insert_emp;

创建往dept表中插入数据的存储过程

#执行存储过程,往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 ;
# drop PROCEDURE insert_dept;

 

5.调用存储过程

dept:

DELIMITER ;
CALL insert_dept(100,10); 

emp:

#执行存储过程,往emp表添加50万条数据
DELIMITER ;    #将 结束标志换回 ;
CALL insert_emp(100001,500000); 
 CALL insert_emp10000(100001,10000); 

6. 大量数据案例

#查询 部门编号为101的,且员工编号小于100100的用户,按用户名称排序

MySQL高级---第二部分【索引优化】_第29张图片

MySQL高级---第二部分【索引优化】_第30张图片

#结论:很显然,type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。

#开始优化:
思路:  尽量让where的过滤条件和排序使用上索引
但是一共两个字段(deptno,empno)上有过滤条件,一个字段(ename)有索引 
1、我们建一个三个字段的组合索引可否?

create index idx_dno_eno_ena on emp(deptno,empno,ename);

MySQL高级---第二部分【索引优化】_第31张图片

MySQL高级---第二部分【索引优化】_第32张图片

我们发现using filesort 依然存在,所以ename 并没有用到索引。
原因是因为empno是一个范围过滤,所以索引后面的字段不会再使用索引了

MySQL高级---第二部分【索引优化】_第33张图片

所以
drop index idx_dno_eno_ena on emp;
但是我们可以把索引建成
create index idx_dno_ena on emp(deptno,ename);

MySQL高级---第二部分【索引优化】_第34张图片

也就是说empno 和ename这个两个字段我只能二选其一。
 这样我们优化掉了 using filesort。
 执行一下sql

MySQL高级---第二部分【索引优化】_第35张图片

速度果然提高了3倍。

 .......

但是 
如果我们建立 
create index idx_dno_eno on emp(deptno,empno); 
而放弃ename使用索引呢?

MySQL高级---第二部分【索引优化】_第36张图片

 果然出现了filesort,意味着排序没有用到索引。
我们来执行以下sql

MySQL高级---第二部分【索引优化】_第37张图片

结果竟然有 filesort的 sql 运行速度,超过了已经优化掉 filesort的 sql ,而且快了近10倍。何故?
原因是所有的排序都是在条件过滤之后才执行的,所以如果条件过滤了大部分数据的话,几百几千条数据进行排序其实并不是很消耗性能,即使索引优化了排序但实际提升性能很有限。  相对的 empno<100100 这个条件如果没有用到索引的话,要对几万条的数据进行扫描,这是非常消耗性能的,所以索引放在这个字段上性价比最高,是最优选择。
 
结论: 当范围条件和group by 或者 order by  的字段出现二选一时 ,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。

5.4 Show Profile

5.4.1是什么

是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量

5.4.2默认情况下,参数处于关闭状态,并保存最近15次的运行结果

5.4.3 分析步骤

1.是否支持,看看当前的mysql版本是否支持

 Show  variables like 'profiling';

MySQL高级---第二部分【索引优化】_第38张图片

2. 开启功能,默认是关闭,使用前需要开启

set profiling=1;

MySQL高级---第二部分【索引优化】_第39张图片

3. 运行SQL 

select * from emp group by id%10 limit 150000;

select * from emp group by id%20  order by 5

4. 查看结果,show profiles;

MySQL高级---第二部分【索引优化】_第40张图片

5. 诊断SQL,show profile cpu,block io for query  n  (n为上一步前面的问题SQL数字号码);

MySQL高级---第二部分【索引优化】_第41张图片

参数备注:

type:  
 | ALL                        --显示所有的开销信息  
 | BLOCK IO                --显示块IO相关开销  
 | CONTEXT SWITCHES --上下文切换相关开销  
 | CPU              --显示CPU相关开销信息  
 | IPC              --显示发送和接收相关开销信息  
 | MEMORY           --显示内存相关开销信息  
 | PAGE FAULTS      --显示页面错误相关开销信息  
 | SOURCE           --显示和Source_function,Source_file,Source_line相关的开销信息  
 | SWAPS            --显示交换次数相关开销的信息

6  日常开发需要注意的结论

converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了

Creating tmp table 创建临时表 ,拷贝数据到临时表,用完再删除

Copying to tmp table on disk 把内存中临时表复制到磁盘,危险!!!

locked

5.5 全局查询日志

5.5.1 配置启用

在mysql的my.cnf中,设置如下:
#开启
general_log=1   
# 记录日志文件的路径
general_log_file=/path/logfile
#输出格式
log_output=FILE

5.5.2 编码启用

#全局日志可以存放到日志文件中,也可以存放到Mysql系统表中。存放到日志中性能更好一些,存储到表中
set global log_output='TABLE';
此后 ,你所编写的sql语句,将会记录到mysql库里的general_log表,可以用下面的命令查看
select * from mysql.general_log;

尽量不要在生产环境开启这个功能。

你可能感兴趣的:(mysql)