7.性能分析和索引优化

sql执行时间长,性能下降的原因推测:
    1.查询语句写的不好
    2.索引失效
        索引分为:
            1.单值索引
                create index idx_user_name on user(name)
            2.多值索引
                create index idx_user_nameEmail on user(name,email)
    3.关联查询太多的join(设计缺陷或者不得已的请求)
   4.服务器调优及各个参数设置(缓存、线程数等) 

sql的执行顺序

执行顺序鱼骨图如下:

7种join


索引

Mysql官方对索引的定义是:索引(Index)是帮助mysql搞笑获取数据的数据结构
索引本质:是一种数据结构
可以简单的理解为:索引是排好序的快速查找数据结构

一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在硬盘上
我们平时说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉树)结构组织的索引,其中包含了聚集索引,次要索引,复合索引,前缀索引,唯一索引
默认都是使用B+数索引,统称为索引

索引的优劣势:
    1.优势
        提高数据检索效率,降低数据库的io成本
        通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗
    2.劣势
        1.实际上索引也是一张表,该表保存了主键和索引字段,并执行实体表的记录,所以索引列也是要占用空间的
        2.虽然索引大大的提高了查询效率,同时也会降低更新表的速度,如对表进行INSERT\UPDATE\DELETE
        3.因为更新表结构是,mysql不仅要保存数据还要保存一下索引文件每次更新添加了索引的字段,都会调整因为更新所带来的键值变化后的索引信息 
        
        
索引分类:
    1.单值索引:
        一个索引值包含单个列,一个表可以有多个单列索引
    2.唯一索引
        索引列的值必须唯一,但允许有空值
    3.复合索引
        即一个索引包含多个列
        
常用命令:
    1.创建索引
        CREATE INDEX 索引名称 ON 表名 (字段,字段):一个字段就是单值索引,多个字段就是多值索引
        ALTER 表名 ADD INDEX 索引名称 ON (字段,字段)
        1.1给表添加一个主键,这就意味着索引值必须唯一,且不能为null
            ALTER TABLE 表名 ADD PRIMARY KEY(id字段);
        1.2该条语句创建的索引值必须唯一(除了NULL外,null可能会出现多次)
            ALTER TABLE 表名 ADD UNIQUE 索引名(字段)
        1.3添加普通索引,索引值可以出现多次
            ALTER TABLE 表名 ADD INDEX 索引名(字段)添加普通索引
        1.4指定索引为FULLTEXT,用于全文索引
            ALTER TABLE 表名 ADD FULLTEXT 索引名称(字段名)
    2.删除索引:
        DROP INDEX 索引名称 ON 表名
    3.查看索引:
        SHOW INDEX FROM 表名
        
        
mysql索引结构:
    1.BTree索引
    2.Hash索引
    3.full-text全文索引
    4.R-Tree索引    
B-TREE的逻辑图如下:
上述图便是b+数的逻辑示意图:
    一个b+树,粉红色地区为一个磁盘块,每个磁盘块都包含了数据项和指针(p1,p2,p3)
    非叶子节点是不存储真实的数据,只存储指引搜索方向的数据项,如17,35并不真实存在于数据表中
查找过程:
    如果查找的数据是29,
    1.首先会将磁盘块1加载到内存,此时发生一次IO,在内存中利用二分法查找确定29在17和35之间,锁定磁盘1的p2指针
    2.通过磁盘1的p2指针将磁盘3加载到内存中,发生第二次IO,29在26和30之间,锁定磁盘3的p2指针
    3.通过指针加载到次磁盘8到内存,发生第三次IO,同时通过二分法查找到29,查询结束,总计三次IO
真实情况:
    3层的B+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提升将是巨大的,如果没有索引,每个数据项都要进行一次IO,那么总共需要上百次的IO,显然成本是非常的高

哪些情况下创建索引:

1.创建索引的情况:
    1.主键自动创建唯一索引
    2.频繁作为查找条件的字段应该创建索引
    3.查询中于其他表关联的字段,外键关系创建索引
    4.频繁更新的字段不适合创建索引:因为每次更新不仅是更新了记录还会更新索引信息
    5.where条件中用不到的字段不要创建索引
    6.单键/组合索引的选择问题:在高并发下倾向于创建组合索引
    7.查询中排序字段,排序字段若通过索引去访问会大大的提高排序速度
    8.查询中统计或者分组字段

2.不创建索引的情况:
    1.表记录太少
    2.经常增删改查的表:因为更新表结构是,mysql不仅要保存数据还要保存一下索引文件每次更新添加了索引的字段,都会调整因为更新所带来的键值变化后的索引信息  
    3.数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的表创建索引
      注意:如果某个数据列包含了很多重复的内容,为他创建索引就没有太大的实际效果

性能分析:Explain

mysql常见的瓶颈:
    1.CPU:CPU在饱和的时候一般发生在数据装入内存或者从磁盘读取数据的时候
    2.IO:磁盘I/O瓶颈发生在状图数据远远大于内存容量的时候
    3.服务器硬件的性能瓶颈:使用top,free,iostat和vmstat来查看系统的性能状态
    
Explain:
    使用EXPLAIN关键字可以模拟mysql优化器执行sql查询语句,从而知道Mysql是如何处理你的sql语句的,
    分析你的查询语句或者表结构的性能瓶颈
    1.使用
     explain +sql语句;
     如原来:select * from user;
     现:explain select * from user;
   2.功能:
       1.表的读取顺序
       2.数据读取操作的操作类型
       3.哪些索引可以使用
       4.哪些所以被实际使用
       5.表之间的引用
       6.每张表有多少行可以被优化器查询 

explain查询出的字段解释:
    1.id
        select查询的序列号,包含一组数字,表示查询中执行select子句或者操作表的顺序
        三种情况:
            1.id相同:执行顺序由上而下
            2.id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
            3.id如果相同,可以认为是一组,从上而下顺序执行
              在所有组中,id值越大,优先级越高,越先执行

2.第二个字段:select_type
    有以下几种可能:
    1.SIMPLE:简单的select查询,查询中不包含子查询或者UNION
    2.PRIMARY:查询中若包含任何复杂的子部分,最外层查询会被标记为PRIMARY
    3.SUBQUERY:在select或者WHERE列表中包含了子查询
    4.DERIVED:在FROM列表中包含了子查询会别标记为DERIVED(衍生),MYSQL会递归执行这些子查询,把结果放在临时表中
    5.UNION:若第二个select初始在UNION之后,则会被标记为UNION,若UNION包含在FROM子句的子查询中,外层select会被标记为:DERIVED
    6.UNION RESULT:从UNION表获取结果的select
    
3.type字段:
    有以下几种可能:
    1.ALL
    2.index
    3.range
    4.ref
    5.eq_ref
    6.const,system
    7.NULL
    从最好到最差的依次是:
        system>const>eq_ref>ref>range>index>ALL
    一般来说,得保障查询至少达到range级别,最好可以达到ref级别
    1.system:表中只有一行记录(等于系统表),这是const(常量)类型的特列,平时不会出现,这个也可以忽略
    
    2.const:表示通过索引依次就可以找到,const用于比较primary kye或者unique索引,因为值匹配一行数据,所以很快
             如将主见置于where列表中,mysql就能将该查询转换为一个常量
             
    3.eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主见或者唯一索引扫描.
    
    4.ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而
           它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
           
    5.range:只检索给定范围的行,使用一个索引来选择行,key列显示使用了哪个索引
             一般就是在你的where语句中出现了between,<,>,in等的查询
             这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某一点,结束于另外一个点,不用扫描全部索引 
             
    6.index:Full Index Scan,index和all区别为index类型只遍历索引树,这通常比all快,因为索引文件通常比数据文件小
             也就是说孙然all和index都是读全表,但是index是从索引中读取的,而all是从硬盘中读取的 
             
     7.all:full table sacn,将遍历全表以找到匹配的行
     备注:一般来说要保障查询至少达到range级别,最好能达到ref
     

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

5.key:实际使用的索引,如果为null,则没有使用索引
       查询中若使用了覆盖索引,则该索引仅出现在key列表中 
       
6.key_len:表示索引中使用的字节数,可以通过该列计算查询中使用的索引长度,在不损失精确性的情况下,长度越短越好
           key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义获得的,不是通过表内检索出的 
           
7.ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或者常量被用于查找索引列上的值


8.rows:根据表统计信息及索引选用情况,大致估算出找到所需记录索要读取的行数(理论上越小越好)


9.extra:包含不适合在其他列展示但是十分重要的额外信息
    分类可以分为:
        (重要)1.Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取
                          Mysql中无法利用索引完成的排序操作称之为"文件排序"  
        (重要)2.Using temporary:使用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于order by和分组查询group by
        (重要)3.Using index:表示相应的select操作中使用了覆盖索引(Covering index),避免访问了表的数据行,效率不错!
                      如果同时出现了using where,表名索引别用来执行索引键值的查找
                      如果没有同时出现using where,表名索引用来读取数据而非执行查找动作
                  覆盖索引:
                      就是select的数据列只用从索引中就能获取到,不必读取数据行,mysql可以利用索引返回select列表中的字段
                      而不必根据索引再次读取数据文件,换句话说查询类要被所建的索引覆盖
                      如创建一个复合索引:
                           CREATE INDEX 索引名称 ON 表名 (字段1,字段1)
                           select 字段1,字段2 from 表名;
                       此时按照复合索引的字段和顺序进行查找,就不会再次读取数据文件,而是利用索引获取返回select列表中的字段
                   注意:
                       1.如果要使用覆盖索引,一定要注意select列表中只能取出需要的列(并且列要在复合索引内)   ,不能select *
                       2.因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降!
       4.Using where:表明使用了where条件
       5.using join buffer:使用了连接缓存
       6.impossible where:where字句的值总是fasle,不能来获取任何元组
       7.select tables optimized away:在没有group by子句的情况下,基于索引优化MIN/MX操作或者对于MyISAM
                                       存储引擎优化count(*)操作,不必等到执行阶段再去计算
                                       查询执行计划生成的阶段即完成优化
       8.distinct:优化distinct操作,找到第一个匹配元组后即停止赵同样值的操作

索引优化

创建一张person表,只给主键创建了索引

查询序号>2养狗的最小人的性命和年龄
explain select name,age from person t where t.pet='dog' and t.order > 2 order by t.birth desc limit 1;

联合索引(复合索引)创建面试:
1.https://www.cnblogs.com/rjzheng/p/12557314.html
2.https://www.cnblogs.com/rjzheng/category/1281020.html

双表索引

1.左连接
    select * from 左表  left join 右表 on 左表.关联字段=右表.关联字段;
    实质上是查询出右表符合条件的记录(一条具体记录),循环左表进行遍历,索引对遍历操作没有什么实质性帮助,主要帮助是排序,
    结论:所以给右表加索引

2.右关联
    select * from 左表  right join 右表 on 左表.关联字段=右表.关联字段;
    左表驱动右表,即查询出左表的记录去遍历右表
    结论:给左表字段加索引
    
join语句的优化:
    1.尽可能减少jon语句中的循环总次数,"永远使用小结果集驱动大的结果集"
    2.优先优化嵌套的内层循环
    3.保证join语句汇总被驱动表上join条件字段已经被索引
    4.当无法保证被驱动表的join条件字段被索引且内存资源充足的前提下,不要吝啬JoinBuffer的设置

 

posted @ 2022-05-25 20:53  努力的达子  阅读(55)  评论(0编辑  收藏  举报