zoukankan      html  css  js  c++  java
  • MySQL索引优化分析(下)

    五、索引优化

    1、索引分析

    (1)、单表

    建表

    CREATE TABLE IF NOT EXISTS article(
    id INT(10) UNSIGNED NOT NULL PRIMARY KEY auto_increment,
    author_id INT(10) UNSIGNED NOT NULL COMMENT'作者id',
    category_id INT(10) UNSIGNED NOT NULL COMMENT'分类id',
    views INT(10) UNSIGNED NOT NULL COMMENT '被查看的次数',
    comments INT(10) UNSIGNED NOT NULL COMMENT'回帖的备注',
    title VARBINARY(255) NOT NULL COMMENT'题目',
    content LONGTEXT NOT NULL COMMENT'内容'
    )
    
    INSERT INTO article(id,author_id,category_id,views,comments,title,content) VALUES
    (NULL,1,1,1,1,'1','1'),
    (NULL,2,2,2,2,'2','2'),
    (NULL,1,1,3,3,'3','3');
    
    
    SELECT * FROM article;

    案列:

    查询category_id为1且comments大于1的情况下,views最多的文章id


    分析:


    结论:type为ALL,即最坏的情况,使用了全表扫描。Extral里还出现了Using filesort,也是最坏的情况,产生了悲剧的文件排序,所以优化是必须的。

    查看表中的索引:


    只有主键,没有别的索引

    开始优化:

    ①、新建索引+删除索引

    创建两种方式:

    ① ALTER TABLE article ADD INDEX idx_article_ccv(category_id,comments,views);
    
    ② CREATE INDEX idx_article_ccv on article(category_id,comments,views)
    


    创建索引之后有所效果但是不好:


    type变为range是可以的忍受的,但是Extral里使用Using filesort仍然是不可以的。

    我们已经建立了索引为啥不行?这是因为按照BTree索引的工作原理,先排序category_id,如果遇到相同的category_id,则再排序comments,如果遇到相同的comments则再排序views。

    当comments在联合索引里处于中间位置的时候,因comments>1是一个范围(所谓的range)

    MySQL无法利用索引再对后面的views部分进行检索,即range类型查询字段后面的索引无效,索引失效。

    删除索引:

    DROP INDEX idx_article_ccv ON article

    再建索引:


    优化后的结果:


    结论:可以看到type变为ref,Extral中的Using filesort也消失了,结果很理想。

    (2)、两表

    建表

    CREATE TABLE IF NOT EXISTS class(
    id INT(10) UNSIGNED NOT NULL auto_increment,
    card INT(10) UNSIGNED NOT NULL COMMENT'分类card',
    PRIMARY KEY (id)
    );
    
    CREATE TABLE IF NOT EXISTS book(
    bookId INT(10) UNSIGNED NOT NULL auto_increment,
    card INT(10) UNSIGNED NOT NULL COMMENT'分类card',
    PRIMARY KEY (bookId)
    );
    
    
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    
    
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO book(card) VALUES(FLOOR(1+RAND()*20));

    案例:对两张表左连接进行优化


    结论:type有ALL。

    开始优化:

    添加索引(将索引添加在右表)


    可以看到第二行的type变为ref,rows也变成了优化比较明显。

    这是左连接特性确定的。LEFT JOIN条件用于确定如何从右表搜索行,左边一定都有,所以右边是关键点,一定要建立索引。

    (左连接索引加右表)

    将索引添加在左表:


    比上面差!

    重新删除索引+新建索引+Explain



    同理,在右连接上,RIGHT JOIN条件用于确定如何从左表搜索行,右边一定都有,所以左边使我们的关键点,一定需要建立索引。

    (3)、三表

    建表

    CREATE TABLE IF NOT EXISTS phone(
    phoneId INT(10) UNSIGNED NOT NULL auto_increment,
    card INT(10) UNSIGNED NOT NULL COMMENT'分类card',
    PRIMARY KEY (phoneId)
    )ENGINE = INNODB;
    
    
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));
    INSERT INTO class(card) VALUES(FLOOR(1+RAND()*20));

    案例


    加索引


    总结:后两行的type都是ref,且总rows优化很好,效果不错。因此索引最好设置在需要经常查询的字段中。

    JOIN语句优化:

    ①、尽可能减少JOIN语句中NestedLoop的循环总次数,“永远用小结果集驱动大结果集”

    ②、优先优化NestedLoop的内循环。

    ③、保证JOIN语句中被驱动表上join条件字段已经被索引。

    ④、当无法保证被驱动表中的join条件字段被索引且内存资源充足的前提下,不要吝啬JoinBuffer的设置

    2、索引失效(应该避免)

    (1)建表

    CREATE TABLE IF NOT EXISTS staffs(
    id INT PRIMARY KEY auto_increment,
    name VARCHAR(24) NOT NULL DEFAULT'' COMMENT'姓名',
    age INT NOT NULL DEFAULT 0 COMMENT'年龄',
    pos VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',
    add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
    )CHARSET utf8 COMMENT'员工记录表';
    
    
    INSERT INTO staffs(name,age,pos,add_time) VALUES('z3',22,'manager',NOW());
    INSERT INTO staffs(name,age,pos,add_time) VALUES('july',23,'dev',NOW());
    INSERT INTO staffs(name,age,pos,add_time) VALUES('2000',23,'dev',NOW());
    
    SELECT * FROM staffs;

    添加索引:

     

    (2)案例(索引失效)

    ①、全职匹配我最爱

    ②、最佳右前缀法则

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



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


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


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


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


    ⑦、is null ,is not null 也无法使用索引


    ⑧、like以通配符开头(‘%abc····’)mysql索引失效会变成全表扫描的操作


    结论:%-写在最右边效果最好


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

    可以用覆盖索引来解决失效的问题。


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


    ⑩、少用or,用它来连接的时候索引会失效


    小总结口诀:

    全值匹配我最爱,最左前缀要遵守;

    带头大哥不能死,中间兄弟不能断;

    索引列上少计算,范围之后全失效;

    like百分写最右,覆盖索引不写星;

    不等空值还有or,索引失效要少用;

    VAR引号不可丢,SQL高级也不难;

    (3)面试题讲解

    建表

    CREATE TABLE IF NOT EXISTS test(
    id INT(10) UNSIGNED NOT NULL PRIMARY KEY auto_increment,
    c1 CHAR(10),
    c2 CHAR(10),
    c3 CHAR(10),
    c4 CHAR(10),
    c5 CHAR(10) 
    );
    
    
    INSERT INTO test(c1,c2,c3,c4,c5) VALUES('a1','a2','a3','a4','a5');
    INSERT INTO test(c1,c2,c3,c4,c5) VALUES('b1','b2','b3','b4','b5');
    INSERT INTO test(c1,c2,c3,c4,c5) VALUES('c1','c2','c3','c4','c5');
    INSERT INTO test(c1,c2,c3,c4,c5) VALUES('d1','d2','d3','d4','d5');
    INSERT INTO test(c1,c2,c3,c4,c5) VALUES('e1','e2','e3','e4','e5');
    INSERT INTO test(c1,c2,c3,c4,c5) VALUES('f1','f2','f3','f4','f5');
    
    
    SELECT * FROM test;

    建索引


    分析:

    1)、


    全匹配,无任何问题,即使改变索引顺序结果也不会改变,MySQL的底层也会进行优化,但是推荐按照顺序写。

    2)


    范围之后全失效,c1,c2能够用到查询,c3只能用到排序没有用到查找,c4无效。


    3)


    索引是两大功能:查找、排序

    用到c1、c2,查找,c3被用于排序,c4失效



    只有c1一个字段索引,c2,c3用于排序,没有filesort。


    我们组建的索引为1234,但是order by没有按照顺序来,出现了filesort。


    最后的没有出现filesort是因为c2已经被作为一个常量确定了。


    group by表面叫分组,但是分组之前必然排序,所以group by 和 order by优化法则类似


    定值、范围还是排序,一般order by是给个范围

    group by一般都需要进行排序,会有临时表产生。

    3、一般性建议

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

    在选择组合索引的时候,当前Query中过滤性最好的字段发生在索引字段顺序中,位置越靠前越好。

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

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

  • 相关阅读:
    STM32F401+nRF24L01无线传输音频(对讲机原型)
    Keil MDK STM32系列(五) 使用STM32CubeMX创建项目基础结构
    Keil MDK STM32系列(四) 基于抽象外设库HAL的STM32F401开发
    Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发
    PlatformIO下的STM32F4xx项目配置
    nRF24L01基于FIFO TX队列的发送性能优化
    个人发展计划IDP 与Smart原则
    IDEA Maven 项目默认编译项目为JDK 1.5
    投资 学习 成长 判断力 面对挫折
    投资 价值 道德 长期主义(5,10,20年)三观
  • 原文地址:https://www.cnblogs.com/huangzhe1515023110/p/9276025.html
Copyright © 2011-2022 走看看