3_MySQL_Explain执行计划

explain 执行计划

    explain + sql 语句

    explain + select * from tbl_user  横向列表展示
    explain + select * from tbl_user \G  纵向列表展示

    作用:1、表的读取顺序
              2、数据读取操作的操作类型
              3、那些索引可以使用
              4、那些索引被实际使用
              5、表之间的引用
              6、每张表有多少行被优化器查询

id         1-select 查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序    
            2-三种情况   这里的id 说的是下面select_type对应的6种类型的数值
                      2.1=id相同,执行顺序由上至下(sql比如是关联查询)
                      2.2=id不同,(子查询)id的序号会递增,id值越大优先级越高
                      2.3=id相同不同,

 select_type    select 关键字对应的那个查询类型 
                    1.SIMPLE 简单的selet 查询,查询中不包含子查询或UNION
                    2.PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为
                    3.SUBQUERY 在select或WHERE列表中包含了子查询
                    4.DERIVED 在FROM类表中包含的子查询被标记为DERIVED(衍生)
                       MySQL会递归执行这些子查询,把结果放在临时表里
                    5.UNION 若第二个SELECT出现在UNION之后,则被标记为UNION
                       若UNION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED
                    6.UNION RESULT 从UNION表获取结果的SELECT

table   显示这一行数据时关于那张表的

type **  访问类型
            ALL     index     range    ref    eq_ref  const   system   NULL
           从最好到最差
           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   :Full Index Scan,Index于All区别为index类型值遍历索引树,这通常比ALL快,因为索引文件通常比
                    数据文件小,(也就是说虽然all和index都是读全表)但index是从索引中读取的
                    而all是从硬盘中读的

possible_keys ** 可能用到的索引
                1.显示可能应用在这张表中的索引,一个或多个
                2.查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
                
 key  ** 实际用到的索引
             1.实际使用的索引,如果为NULL,则没有使用索引
              2.查询中若使用了覆盖索引,则该索引仅出现在key列表中
        
key_len    
            1.表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好
            2.ken_len的值为索引字段的最大可能长度,并非实际使用长度,即ken_len是根据表定义计算而得,不是通过表内检索                    出的
        
ref        显示索引的那一列被使用了,如果可能的话,是一个常数,那些列或常量被用于查找索引列上的值
        
rows     根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

Extra    包含不适合在他列中显示但十分重要的额外信息
                
             1.Using filesort  :说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取
                (不好)                   MySQL中无法利用索引完成的排序操作成为“文件排序”
                
              2.Using tempporay : 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表,常见于排序order by 和分组查询group by.
             (不好)
              3.USING index  :表示相应的select操作中使用了覆盖索引(Covering index) ,避免访问了表的数据行,效率不错
                                           如果同时出现using where ,表明索引被用来执行索引键值的查找
                                           如果没有同时出现using where ,表明索引用来读取数据而非执行查找动作
                                    
                                    覆盖索引(Covering Index) :
                                    方式一
                                    就是select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,
                                    而不必根据索引再次读取数据文件,换句话说,查询列要被建的索引覆盖
                                    
                                    ** 如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select * 
                                       因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降
                
              4.Using where        表明使用了where 过滤
                
              5.using join buffer 使用了连接缓存(可以调大join_buffer)
                
              6.impossible where  where 子句的值总是false,不能用来获取任何元素
                
              7.select tables optimized away  没有group by 子句的情况下,基于索引优化MIN/MAX操作或者
                                                对于MyISAM存储引擎优化Count(*)操作,不必等到执行阶段在进行计算
                                                查询执行计划生成阶段即完成优化
                
                8.distinct            在找到第一个匹配的元祖后即停止找同样值得操作

开始优化

   CREATE TABLE IF NOT EXISTS 'article'(
    id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    author_id INT(10) UNSIGNED NOT NULL,
    category_id INT(10) UNSIGNED NOT NULL,
    views INT(10) UNSIGNED NOT NULL,
    comments INT(10) UNSIGNED NOT NULL,
    title VARBINARY(255) NOT NULL,
    content TEXT NOT NULL,
    )
    
    INSERT INTO article(author_id,category_id,views,comments,title,content) VALUES
    (1,1,1,1,'1','1'),
    (2,2,2,2,'2','2'),
    (1,1,3,3,'3','3'),

案例一、

##查询category_id 为1 且comments大于1的情况下,views最多的article_id                    
    EXPLAIN SELECT id,author_id, FROM article WHERE category_id=1 AND comment > 1 ORDER BY views desc LIMIT 1;

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

1.1 新建索引+删除索引
        ALTER TABLE 'acticle' ADD INDEX idx_article_ccv on ('category_id','comment','viesw');
        create index idx_arcticle_ccv on article(category_id,comment,views);
        执行案例1的sql
        DROP INDEX idx_article_ccv  ON article;

1.2 第2次EXPLAIN
        EXPLAIN SELECT id,author_id FROM article where category_id=1 AND comment > 1 ORDERY BY views desc LIMIT 1;
    
        EXPLAIN SELECT id,author_id FROM article where category_id=1 AND comment = 3 ORDERY BY views desc LIMIT 1;

结论;
        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_ccv('category_id','views');
        create index idx_article on article(category_id,views);
    
1.5 第3次EXPLAIN
        EXPLAIN SELECT id,author_id FROM article WHERE category_id=1 AND comment >1 ORDER BY views DESC LIMIT 1;
    
    结论:可以看到,type变成了ref,Extra中的Using filesort也消失了,结果非常理想
        DROP INDEX idx_article_cv ON article;

案例二、两个表
    CREATE TABLE NOT EXISTS class(
        id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
        card_INT(10) UNSIGNED NOT NULL,
        PRIMARY KEY ID
    )
        
    CREATE TABLE IF NOT EXSITS book(
        bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
        card INT(10) UNSIGNED NOT NULL,
        PRIMARY KEY(bookid)
    )
    
    INSERT INTO class(card) VALUES(floor(1+(RAND()*20)));
    
    INSERT INTO book(card) VALUES(floor(1+(RAND()*20)));
    分别插入20条数据

第一次Explain
    Explain SELECT * FROM class LEFT JOIN book ON class.card= book.card
    #结论:type 有All

添加索引优化
    ALTER TABLE book Add iNDEX Y(card) 
    第二次Explian 
    Explain SELECT * FROM class LEFT JOIN book ON class.card= book.card
    #可以看到第二行的type变为了ref,rows也成了优化比较明显
    #这是由左连接特性决定的,LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有
    #所以右边是关键点,一定需要建立索引

#删除旧索引 + 新建 +第三次explain
    DROP INDEX Y  ON book
    ALTER TABLE class ADD INDEX X (card)
    EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card

然后来看一个右连接查询
    优化比较明显,这是因为RIGHT JOIN条件用于确定如何从左表搜索行,右边一定都有,
    所以左边是关键点,一定需要建立索引
    EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card
    DROP INDEX  X ON class;
    ALTER TABLE book ADD INDEX Y(card)
    #右连接,基本无变化
    EXplian select * from class RIGHT JOIN book on class.card = book.card 

案例三、三张表

三表
    CREATE TABLE IF NOT EXISTS phone(
        phoneid INT(10) UNSIGNED NULL AUTO_INCREMENT,
        card INT(10) UNSIGNED NOT NULL,
        PRIMARY KEY(phoneid)
    )ENGINE=INNODB;

插入20条记录
    INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));

删除原来旧索引
添加新的索引 ALTER TABLE phone ADD INDEX z(card)

ALTER TABLE book ADD INDEX Y(card) #上一个case建过一个同样的
    Explain select * from class LEFT JOIN book ON class.card=book.card 
            LEFT JOIN phone ON book.card=phone.card 
        
    #后2行的type都是ref且rows优化很好,效果不错,因此索引最好设置在需要经常查询的字段中

结论
        join语句的优化
        
        1.尽可能减少join语句中的NestedLoop循环次数:“永远用小结果集驱动打的结果集”
        2.优先优化NestedLoop的内层循环
        3.保证Join语句中被驱动表上Join条件字段已经被索引;
        
        当无法保证驱动表的Join条件字段被索引且内存资源充足的前提下,不要太利息JoinBuffer的设置

你可能感兴趣的:(MySQL)