MySQL优化:索引失效分析in与exists使用场合

Posted 脚踏实地,尝试创新

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL优化:索引失效分析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的设置。

 

以上是关于MySQL优化:索引失效分析in与exists使用场合的主要内容,如果未能解决你的问题,请参考以下文章

SQL优化- 数据库SQL优化——使用EXIST代替IN

MySQL 高级--优化 —— 创建索引原则使用场景和索引失效的情况

MYSQL IN 与 EXISTS 的优化示例

SQL查询中in和exists的区别分析

MySQL高级 之 索引失效与优化详解

MySQL索引优化与查询优化(重点:索引失效的11种情况)