• Mysql语句优化


    mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。exists与in性能的好坏不能一概而论,要根据表的大小,索引来决定。

    一.

    如果查询的两个表大小相当,那么用in和exists差别不大。 

    如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in: 

    例如:表A(小表),表B(大表)

    1:

    select * from A where cc in (select cc from B) 效率低,用到了A表上cc列的索引。

    上面的查询首先会从B中查出所有的cc,因为B表是大表,所以会select cc from B会产生许多的cc(而且会全表扫描B表),然后这些cc值按照顺序一个一个从A表取出相应的记录,所以只会用到A表的索引,主要的开销在全表扫描B上。

    select * from A where exists(select cc from B where cc=A.cc) 效率高,用到了B表上cc列的索引。 

    上面的查询首先会全表扫描A,得到A的所有记录,然后用每条记录中的cc值作为查询条件在B表中查询,由于有索引,所以查询很快,避免的大表的全表扫描,只扫描了小表A,因此效率比较高。

    2:

    select * from B where cc in (select cc from A) 效率高,用到了B表上cc列的索引;

    同样,上述查询只全表扫描了A,大表查询用的是索引。

    select * from B where exists(select cc from A where cc=B.cc) 效率低,用到了A表上cc列的索引。 

    当A和B表在cc列上都没有索引时,in和exists都需要全表扫描A表和B表,查询速度差别不大。

     not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快

     

    二.优化group by和order by

    分组和排序算是mysql中比较消耗cpu的操作,当我们用explain工具来查看sql时,往往会看到Extra那一栏都会出现using temporary,using filesort(临时表和文件排序),因为得到的数据并非是已经分组的和排序的,需要额外的空间来进行分组排序,我们可以通过建立合适的索引来优化,因为索引是已经排序的。举例说明:

    假设有表A,有如下几条数据:字段是a(主键),b,c,d。

    a                 b                   c                         d

    1                 1                   2                         3

    2                 2                   3                         1

    3                 1                   2                         2

    4                 1                   2                         5

    5                 1                   2                         4

    我们执行select * from A where b<2 order by d desc

    因为b字段没有索引,所以需要全表扫描A,那么扫描过后得到数据

    1                 1                   2                         3

    3                 1                   2                         2

    4                 1                   2                         5

    5                 1                   2                         4

    此时d字段是乱序的,那么mysql需要临时开辟一段内存空间(临时表)来保存扫描后的结果,然后对d字段进行排序(文件排序)。

    如果我们在d字段加上索引,那么可以按照索引的顺序来扫描全表,扫描后的结果就是排序的结果。

    d字段的索引是这样的:

    5  4

    4  5

    3  1

    2  3

    1      2

    上面每一行的前一个数是d字段的值,后一个字段为d字段所在行的主键a的值。按照d字段索引的顺序扫描全表,首先扫描a=4的行,发现这一行的b<2,满足条件,则保留

    4                 1                   2                         5

    接下来扫描a=5的行,发现这一行的b<2,满足条件,则保留

    4                 1                   2                         5

    5                 1                   2                         4

    。。。。。。。。。

    接下来扫描a=2的行,发现这一行的b=2,不满足,则舍弃

    4                 1                   2                         5

    5                 1                   2                         4

    1                 1                   2                         3

    3                 1                   2                         2

    扫描过后的记录就是符合条件的记录,不需要临时表和排序。对于gropup来说,也可以用这样的方法进行优化,因为字段已经排序,可以直接在获得的记录中进行分组计算。

    三.

    几个原则:

    1. 小表(记录在几万以内)查询,无论有无索引,查询性能相差不大。
    2. 和大表做join,关联字段上一定要加索引
    3. join操作一般用小表驱动大表
    4. 尽量不要使用like,regex,or
    5. 尽量不要在查询的列上做计算,如where length(detail)>15 detail字段的长度大于15,这种条件都会进行全表扫描,即使detail有索引。
    6. 尽量不给大字段做索引,比如 detail varchar(256),这种索引耗费cpu,耗费空间,而且查询效率低。如果业务允许的话,可以建个前缀索引。
  • 相关阅读:
    面试题29:数组中出现次数超过一半的数字
    面试题25:二叉树中和为某一值的路径
    Path Sum II
    面试题28:字符串的排列
    面试题24:二叉搜索树的后序遍历序列
    面试题23:从上往下打印二叉树
    面试题22:栈的压入、弹出序列
    面试题20:顺时针打印矩阵
    面试题18:树的子结构
    Linux 中使用 KVM
  • 原文地址:https://www.cnblogs.com/CLFR/p/6409161.html
Copyright © 2020-2023  润新知