zoukankan      html  css  js  c++  java
  • MySQL优化(5):索引失效分析、in与exists使用场合

    一、索引失效的情况

      前文提及过可以通过explain的possible_keys、key属性判断索引是否失效,key如果为null,可能是索引没建,也可能是索引失效,下面列举一些会使索引失效的情况。

    1、全值匹配:顺序、个数与索引一致

    2、最佳左前缀法则:查询从索引的最左前列开始并且不跳过索引中的列,中间跳过的值,后面的索引会失效

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

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

      

      name字段用于查找,age>11也用到了,但着重用于排序,pos则没用到索引

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

      using where是在表里检索,using index会直接从索引里检索

       

      这里也是范围检索,但与上面不同的是这里从索引里获取数据,没有用到age

       

    6、mysql在使用不等于(!= 或 <>)时无法使用索引

       

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

    8、like以通配符开头(‘%abc..’)也会导致索引失效

      通过覆盖索引可以解决like '%字符串%'索引失效的问题

      例:假设以name,age字段建索引

    create index idx_user_nameAge on tb_user(name,age);

      查询字段只要有一个和覆盖索引沾边就行

       

      

      但如果有超过索引的部分,索引就用不上了,所以用select * 就不能使用覆盖索引

       

    9、字符串不加单引号,该字段以后的索引失效

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

       

    11、少数据值的列也不应该增加索引,只有两种情况,且平均分布,加了索引反而降低速度

    12、range的包含范围有一定的阈值,超过会进行全文扫描

     二、in与exists使用场合

      坚持小表驱动大表的原则

    in:当B表的数据集必须小于A表的数据集时,in优于exists 

    select * from A where id in (select id from B)
    #等价于:
      for select id from B
      for select * from A where A.id = B.id

    exists:当A表的数据集小于B表的数据集时,exists优于in

      将主查询A的数据,放到子查询B中做条件验证,根据验证结果(true或false)来决定主查询的数据是否保留

      子查询也可以用条件表达式、其他子查询或join来替代,何种最优需具体问题具体分析

    select * from A where exists (select 1 from B where B.id = A.id)
    #等价于
        for select * from A
        for select * from B where B.id = A.id
    #A表与B表的ID字段应建立索引

    三、对Order By的优化

    1、用order by子句的重点是是否会产生filesort。建索引时已经排好序,所以order by的顺序和索引最好一致,避免再一次排序。    

        

      所建的索引默认升序,一升序一降序会产生内排序

       

    2、状态最好是using index,让mysql通过扫描索引本身完成排序。

      能使用index方式排序的情况:order by语句使用索引最左前列,或where子句与order子句条件组合满足索引最左前列。

     (1)order by语句使用索引最左前列,order by后字段同为asc或desc都行

        

     (2)加上where子句的条件与order by子句条件列组合满足索引最左前列

       

     (3)不能使用索引的情况

       

       假如以category_id、comments、views的顺序建索引

      

    3、filesort的两种算法

     (1)双路排序:两次扫描磁盘(读取行指针和order by列,对他们进行排序,然后扫描已排好序的列表,重新列表读取数据输出)。

     (2)单路排序:mysql4.1版本后,从磁盘读取查询需要的所有列,按order by列在buffer对它们排序,然后扫描排序后的列表输出,只读取一次数据,且把随机IO变为顺序IO,但会使用更多空间,因为它把每一行都保存在内存中。

        单路排序存在的问题:

        因为要把所有字段取出,可能要取出的大小超出sort_buffer容量,导致每次只能取sort_buffer容量大小的数据进行排序(创建tmp文件,多路合并),排完再取sort_buffer容量大小的数据,反而会导致更多I/O操作。

    4、order by优化策略:

     (1)单路多路算法的数据都有可能超过sort_buffer_size,超出后会建tmp文件进行合并排序,导致多次I/O,可以根据系统能力增大sort_buffer_size参数设置

     (2)增大max_length_for_sort_data参数,会增加用单路排序的概率,但如果设太大,也会更容易使数据超过sort_buffer_size,当query的字段大小总和小于max_length_for_sort_data且排序字段不是text/blob类型时,才会用单路排序,否则还是用多路排序。

     (3)order by时不要用select *,只select需要的字段,多余的字段会占用sort_buffer的内存

    5、group by:  

      适用order by原则,实质先排序后分组,遵守索引建的最佳左前缀,使用不当会产生临时表。

       

      当无法使用索引列,增大max_length_for_sort_data和sort_buffer_size参数设置。能在where的条件就不放在having里。

    四、案例,其他注意点

      假如以c1,c2,c3,c4的顺序建立索引

    1、对于常量类型,查询优化器会自动调优SQL,顺序不影响

       

    2、范围之后全失效,但查询优化器会先常量类型自动调优,c3被调前,c4后的失效,但c4是最后一个了,所以仍用到4个。以上的例子中间并没有断

       

    3、都只用到了c1,c2,第三条语句无法使用到索引排序,所以mysql内部自己进行了一次排序(前两个c3没用到查找,但用到了排序,所以无using filesort,只是没有记录到key_len里)

       

    4、order by不按索引顺序会出现using filesort,本来照理第一个按order by c3,c2排序会出现filesort,但是前面已经有c2=‘a2’的条件,c2已经是常量值,所以c2其实不用排序

       

     五、多表连接在从表加索引可以提高速度

    案例1:两表连接的情况,多表连接时在主表还是从表建索引的问题

      如未使用索引的情况

       

      左连接把索引建在从表的关联字段比较好,主表一定会有,从表才是检索的关键

       

    案例2:三表关联要建在哪些字段上

      没建索引的时候

       

      在第二、三个从表的关联字段加索引

    ALTER TABLE `phone` ADD INDEX z(`card`);
    ALTER TABLE `book` ADD INDEX Y(`card`); 

        

    结论:

      (1)join语句中被驱动表上join条件字段加索引可以提高效率;

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

     

  • 相关阅读:
    [转载]Markdown基础语法
    [转载]转发和重定向的区别
    浅谈网站搭建中的内网穿透、端口转发等知识点
    [转载]HTTP和HTTPS
    [转载]为什么有些语言可以被反编译?而有的不能?
    例10-2 uva12169(扩展欧几里得)
    例10-1 uva11582(裴波那切数列)
    例10-7 uva10820(欧拉)
    hdu 5137(2014广州—最短路)
    hdu 5135(2014广州—状态dp)
  • 原文地址:https://www.cnblogs.com/zjxiang/p/9160810.html
Copyright © 2011-2022 走看看