MySQL索引的优化和分析

1. 性能下降SQL慢 执行时间长 等待时间长

1. 数据过多

分库分表

2. 关联表过多

SQL优化

3. 没有充分利用到索引

建立索引

4. 服务器调优与各个参数设置

调整my.cnf

2. 常见的JOIN 查询

1. 7中join查询在这里插入图片描述

3. 索引简介

1. 索引是什么

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
可以得到索引的本质:索引是数据结构。

使用的是B + 树  

一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上

简单说就是方便快速查询和排序的字段

2. 索引的优势

1. 类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本
2. 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗

3. 索引的劣势

1. 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERTUPDATEDELETE。
因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,
都会调整因为更新所带来的键值变化后的索引信息
2. B + 树 的叶子节点  其实就是一个key value的结果 索引的字段值 所以索引列也是要占用空间的

4. mysql索引结构

数据结构图

在这里插入图片描述

B+Tree与B-Tree 的区别
 1B-树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;B+树的非叶子节点中只有关键字和指向下一个节点的索引,记录只放在叶子节点中。
  2)在B-树中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;而B+树中每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。从这个角度看B-树的性能好像要比B+树好,而在实际应用中却是B+树的性能要好些。因为B+树的非叶子节点不存放实际的数据,这样每个节点可容纳的元素个数比B-树多,树高比B-树小,这样带来的好处是减少磁盘访问次数。尽管B+树找到一个记录所需的比较次数要比B-树多,但是一次磁盘访问的时间相当于成百上千次内存比较的时间,因此实际中B+树的性能可能还会好些,而且B+树的叶子节点使用指针连接在一起,方便顺序遍历(例如查看一个目录下的所有文件,一个表中的所有记录等),这也是很多数据库和文件系统使用B+树的缘故。 
 
思考:为什么说B+树比B-树更适合实际应用中操作系统的文件索引和数据库索引? 
1) B+树的磁盘读写代价更低 
  B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。 
2) B+树的查询效率更加稳定 
  由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
聚簇索引与非聚簇索引
聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。
术语‘聚簇’表示数据行和相邻的键值聚簇的存储在一起。
 如下图,左侧的索引就是聚簇索引,因为数据行在磁盘的排列和索引排序保持一致。

 
聚簇索引的好处:
按照聚簇索引排列顺序,查询显示一定范围数据的时候,由于数据都是紧密相连,数据库不不用从多个数据块中提取数据,所以节省了大量的io操作。
聚簇索引的限制:
对于mysql数据库目前只有innodb数据引擎支持聚簇索引,而Myisam并不支持聚簇索引。
由于数据物理存储排序方式只能有一种,所以每个Mysql的表只能有一个聚簇索引。一般情况下就是该表的主键。
为了充分利用聚簇索引的聚簇的特性,所以innodb表的主键列尽量选用有序的顺序id,而不建议用无序的id,比如uuid这种。

MySQL中就主键索引是聚簇索索引 其他索引都是非聚簇索引

5. mysql索引分类

1. 单值索引
2. 唯一索引
3. 主键索引
4. 复合索引
5. 全文索引(Myisam支持)

6. 哪些情况需要创建索引

  1. 主键自动建立唯一索引
  2. 频繁作为查询条件的字段应该创建索引
  3. 查询中与其它表关联的字段,外键关系建立索引
  4. 单键/组合索引的选择问题, 组合索引性价比更高
  5. 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
  6. 查询中统计或者分组字段

7. 哪些情况不要创建索引

  1. 表记录太少

  2. 经常增删改的表或者字段

    ​ 提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。
    ​ 因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件

  3. Where条件里用不到的字段不创建索引

  4. 过滤性不好的不适合建索引

4.性能分析 Explain

是什么

使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是
如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈

能干嘛

表的读取顺序
哪些索引可以使用
数据读取操作的操作类型
哪些索引被实际使用
表之间的引用
每张表有多少行被物理查询

怎么用

Explain + SQL语句

在这里插入图片描述

个字段解释

id

select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

三种情况:

  1. id相同,执行顺序由上至下
  2. id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  3. id有相同,也有不同,同时存在。id相同的可以认为是一组,从上往下顺序执行;在所有的组中,id的值越大,优先级越高,越先执行
select_type
  1. 有哪些
    在这里插入图片描述
    查询的类型,主要是用于区别
    普通查询、联合查询、子查询等的复杂查询
    1. SIMPLE
      简单的 select 查询,查询中不包含子查询或者UNION
      在这里插入图片描述

    2. PRIMARY

      查询中若包含任何复杂的子部分,最外层查询则被标记为Primary
      在这里插入图片描述

    3. DERIVED

      在FROM列表中包含的子查询被标记为DERIVED(衍生) MySQL会递归执行这些子查询, 把结果放在临时表里。
      在这里插入图片描述

    4. SUBQUERY
      在SELECT或WHERE列表中包含了子查询
      在这里插入图片描述

    5. DEPENDENT SUBQUERY

      在SELECT或WHERE列表中包含了子查询,子查询基于外层
      在这里插入图片描述

    6. UNCACHEABLE SUBQUREY
      在这里插入图片描述

    7. UNION
      若第二个SELECT出现在UNION之后,则被标记为UNION; 若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
      在这里插入图片描述

    8. UNION RESULT
      从UNION表获取结果的SELECT
      在这里插入图片描述

table

显示这一行的数据是关于哪张表的

partitions

代表分区表中的命中情况,非分区表,该项为null (数据库是否分区 没分区都是null)

type 在这里插入图片描述
访问类型排列
type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是: 
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 
 
 
一般来说,得保证查询至少达到range级别,最好能达到ref。

显示查询使用了何种类型,
从最好到最差依次是:
system>const>eq_ref>ref>range>index>ALL
类型 解释
system 表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计
const 表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快
如将主键置于where列表中,MySQL就能将该查询转换为一个常量
eq_ref 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
ref 非唯一性索引扫描,返回匹配某个单独值的所有行.
本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,
它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
range 只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引
一般就是在你的where语句中出现了between、<、>、in等的查询
这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引。
index 出现index是sql使用了索引但是没用通过索引进行过滤,一般是使用了覆盖索引或者是利用索引进行了排序分组
all Full Table Scan,将遍历全表以找到匹配的行
index_merge 在查询过程中需要多个索引组合使用,通常出现在有 or 的关键字的sql中
ref_or_null 对于某个字段既需要关联条件,也需要null值得情况下。查询优化器会选择用ref_or_null连接查询。
index_subquery 利用索引来关联子查询,不再全表扫描。
unique_subquery 该联接类型类似于index_subquery。 子查询中的唯一索引
possible_keys

显示可能应用在这张表中的索引,一个或多个。
查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

key

实际使用的索引。如果为NULL,则没有使用索引

查询中若使用了覆盖索引,则该索引和查询的select字段重叠

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。

key_len字段能够帮你检查是否充分的利用上了索引

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值

rows

rows列显示MySQL认为它执行查询时必须检查的行数。 越少越好

filtered

这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数

extra

包含不适合在其他列中显示但十分重要的额外信息

属性 解释
Using filesort 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
MySQL中无法利用索引完成的排序操作称为“文件排序”
Using temporary 使了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。
USING index 表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!
如果同时出现using where,表明索引被用来执行索引键值的查找;
如果没有同时出现using where,表明索引只是用来读取数据而非利用索引执行查找。
利用索引进行了排序或分组
Using where 表明使用了where过滤
using join buffer 使用了连接缓存:
impossible where where子句的值总是false,不能用来获取任何元组
select tables optimized away 在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者
对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,
查询执行计划生成的阶段即完成优化。

5. 查询优化

1. 单表使用索引及常见索引失效

	案例(索引失效)
		全值匹配我最爱
			建立索引
		最佳左前缀法则
			如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
		不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
		存储引擎不能使用索引中范围条件右边的列
		mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描
		is not null 也无法使用索引,但是is null是可以使用索引的
		like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作
		字符串不加单引号索引失效
		小总结
	一般性建议
		对于单键索引,尽量选择针对当前query过滤性更好的索引
		在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
		在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引
		在选择组合索引的时候,如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面
		书写sql语句时,尽量避免造成索引失效的情况

2. 关联查询优化

1、保证被驱动表的join字段已经被索引
2、left join 时,选择小表作为驱动表,大表作为被驱动表。
3、inner join 时,mysql会自己帮你把小结果集的表选为驱动表。
4、子查询尽量不要放在被驱动表,有可能使用不到索引。
5、能够直接多表关联的尽量直接关联,不用子查询。

3. 子查询优化

尽量不要使用not in  或者 not exists   用left outer join  on  xxx is null 替代

  EXPLAIN SELECT SQL_NO_CACHE age,count(*) FROM  emp a LEFT OUTER JOIN dept b ON a.id =b.ceo
  WHERE    b.ceo IS   NULL
  group by age 
  having count(*)<10000

4. 排序分组优化

ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序
索引的选择
	执行案例前先清除emp上的索引,只留主键
	索引的选择
如果不在索引列上,filesort有两种算法:
mysql就要启动双路排序和单路排序
	双路排序
		MySQL 4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,
读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出
		从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。
	取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法,就是单路排序。
	单路排序
		从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,
它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间,
因为它把每一行都保存在内存中了。
	结论及引申出的问题
		由于单路是后出的,总体而言好过双路
		但是用单路有问题
	优化策略
		增大sort_buffer_size参数的设置
		增大max_length_for_sort_data参数的设置
		减少select 后面的查询的字段。
		Why
GROUP BY关键字优化
	group by 使用索引的原则几乎跟order by一致 ,唯一区别是groupby 即使没有过滤条件用到索引,也可以直接使用索引。

case

 create index idx_age_deptid_name on emp (age,deptid,name)
 
以下  是否能使用到索引,能否去掉using filesort
1explain  select SQL_NO_CACHE * from emp order by age,deptid; 
2explain  select SQL_NO_CACHE * from emp order by age,deptid limit 10; 
 
 #无过滤 不索引
3explain  select * from emp where age=45 order by deptid;
4explain  select * from emp where age=45 order by   deptid,name; 
5explain  select * from emp where age=45 order by  deptid,empno;
6explain  select * from emp where age=45 order by  name,deptid; 
7explain select * from emp where deptid=45 order by age;
 
#顺序错,必排序
8explain select * from emp where age=45 order by  deptid desc, name desc ;
9explain select * from emp where age=45 order by  deptid asc, name desc ;
  #方向反 必排序

5. 覆盖索引

什么是覆盖索引?
简单说就是,select 到 from 之间查询的列 <=使用的索引列+主键
 
explain select * from emp where name like '%abc';
使用覆盖索引后

6. 驱动表与被驱动表

驱动表  left join 被驱动表
被驱动表  right join 驱动表
表 inner join 表 由MySQL自己选择
驱动表 straight_join 被驱动表 

7. straight_join

作用: inner join 相同 但是可以指定 驱动表与被驱动表  叫直链接
使用注意: 
1. 明确概念
2. 确认两表的数据量关系
驱动表 straight_join 被驱动表 

猜你喜欢

转载自blog.csdn.net/weixin_43939924/article/details/110734315