美文网首页
Mysql语句优化的原则——让你写sql更加顺手

Mysql语句优化的原则——让你写sql更加顺手

作者: 史史小子 | 来源:发表于2017-12-22 09:33 被阅读151次

    使用索引的原则:

    1.最左前缀匹配原则。

    mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。所以要尽量把这些条件放在最后,把“=”条件放在前面。

    不会用到b的索引:

    where a=1 and c>0 and b=2

    会用到b的索引:

    where a=1 and b=2 and c>0

    2.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少。

    3.当取出的数据超过全表数据的20%时,不会使用索引。

    4.使用like时注意:

    不使用索引:

    like ‘%L%’

    使用索引:

    like ‘L%’

    5.尽量将or 转换为 union all

    不使用索引:

    select * from user where name=’a’ or age=’20’

    使用索引:

    select * from user where name=’a’ union all select * from user where age=’20’

    6.字段加函数不会使用索引。所以尽量把函数放在数值上

    不使用索引:

    where truncate(price) = 1

    使用索引:

    where price > 1 and price < 2

    7.如果使用数字作为字符,则数字需要加引号,否则mysql会自动在列上加数据类型转换函数

    不使用索引

    where mobile=18534874321

    使用索引

    where mobile=’18534874321’

    8.字段加运算符不会使用索引。所以尽量把运算放在数值上

    不使用索引:

    SELECT ACCOUNT_NAME, AMOUNT

    FROM TRANSACTION

    WHERE AMOUNT + 3000 >5000;

    使用索引:

    SELECT ACCOUNT_NAME, AMOUNT

    FROM TRANSACTION

    WHERE AMOUNT > 2000 ;

    9.使用组合索引时,必须要包括第一个列。

    例如

    alter table test add index(a,b,c):

    不使用索引:

    where b=1, c=2

    where b=1

    where c=2

    使用索引:

    where a=1, b=1, c=2

    where a=1, b=1

    where a=1, c=2

    10.尽量避免使用is null或is not null

    不使用索引:

    SELECT …

    FROM DEPARTMENT

    WHERE DEPT_CODE IS NOT NULL;

    使用索引:

    SELECT …

    FROM DEPARTMENT

    WHERE DEPT_CODE >0;

    11.不等于(!=)不会使用索引

    不使用索引:

    SELECT ACCOUNT_NAME

    FROM TRANSACTION

    WHERE AMOUNT !=0;

    使用索引:

    SELECT ACCOUNT_NAME

    FROM TRANSACTION

    WHERE AMOUNT >0;

    12.ORDER BY 子句只在以下的条件下使用索引:

    lORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.

    lORDER BY中不能既有ASC也有DESC

    例如:

    alter table t1 add index(a,b);

    alter table t1 add index(c);

    不使用索引:

    select * from t1 order by a,c; 不在一个索引中

    select * from t1 order by b; 没有出现组合索引的第一列

    select * from t1 order by a asc, b desc; 混合ASC和DESC

    select * from t1 where a=1 order by c; where和order by用的不是同一个索引,where使用索引,order by不使用。

    使用索引:

    select * from t1 order by a,b;

    select * from t1 order where a=1 order by b;

    select * from t1 order where a=1 order by a,b;

    select * from t1 order by a desc, b desc;

    select * from t1 where c=1 order by c;

    13.索引不是越多越好。mysql需要资源来维护索引,任何数据的变更(增删改)都会连带修改索引的值。所以,需要平衡考虑索引带来的查询加速和增删改减速。

    其他注意事项

    1.尽量避免使用select *

    2.尽量使用表连接(join)代替子查询select * from t1 where a in (select b from t2)

    3.性能方面,表连接 > (not) exists > (not) in

    1)用exists代替in

    低效:

    SELECT *

    FROM EMP

    WHERE EMPNO > 0

    AND DEPTNO IN (SELECT DEPTNO

    FROM DEPT

    WHERE LOC = ‘MELB’)

    高效:

    SELECT *

    FROM EMP

    WHERE EMPNO > 0

    AND EXISTS (SELECT ‘X’

    FROM DEPT

    WHERE DEPT.DEPTNO = EMP.DEPTNO

    AND LOC = ‘MELB’)

    2)用not exists代替not in

    低效:

    SELECT …

    FROM EMP

    WHERE DEPT_NO NOT IN (SELECT DEPT_NO

    FROM DEPT

    WHERE DEPT_CAT=’A’);

    高效:

    SELECT ….

    FROM EMP E

    WHERE NOT EXISTS (SELECT ‘X’

    FROM DEPT D

    WHERE D.DEPT_NO = E.DEPT_NO

    AND DEPT_CAT = ‘A’);

    3)用表连接代替exists

    exits:

    SELECT ENAME

    FROM EMP E

    WHERE EXISTS (SELECT ‘X’

    FROM DEPT

    WHERE DEPT_NO = E.DEPT_NO

    AND DEPT_CAT = ‘A’);

    表连接:

    SELECT ENAME

    FROM DEPT D,EMP E

    WHERE E.DEPT_NO = D.DEPT_NO

    AND DEPT_CAT = ‘A’ ;

    4.清除不必要的排序

    低效:

    select count(*) from (select * from user where id > 40 order by id);

    高效:

    select count(*) from (select * from user where id > 40);

    5.having -> where

    避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销.

    低效:

    select * from user group by id having id > 40;

    高效:

    select * from user where id > 40 group by id;

    6.除非确实需要去掉重复的行,否则尽量使用union all而不是union。因为union会自带distinct操作,代价很大

    使用explain查看sql性能

    1.explain用法:在select之前加上explain即可。

    例如:explain select * from test;

    注意:explain并不会真正运行语句,而是只返回执行计划。

    怎么看执行计划?一个简单的优化原则:令sql读取尽可能少的行。

    2.实战案例1:

    问题语句运行超过5s:

    SELECT `branch`.`id`, `branch`.`name`, `branch`.`registered_time`, `branch_region`.`region_id`, `user`.`username`, `user`.`mobile`, count(o.order_id) as order_num

    FROM (`branch`)

    LEFT JOIN `user` ON `user`.`branch_id` = `branch`.`id`

    LEFT JOIN `branch_role` ON `branch_role`.`id` = `user`.`role_id`

    LEFT JOIN `branch_region` ON `branch_region`.`branch_id` = `branch_role`.`branch_id`

    LEFT JOIN `orders` o ON `branch`.`id` = `o`.`supplier_id`

    WHERE branch.id NOT IN (select supplier_id from signing where seller_id=6683 and status < 6)

    AND `branch`.`group` = 'SUPPLIER'

    AND `branch_role`.`flag` = 'ADMINISTRATOR'

    AND `branch`.`status` = 'NORMAL'

    GROUP BY `branch`.`id`

    ORDER BY `branch`.`registered_time` desc

    LIMIT 20;

    使用explain查看执行计划:

    根据“读取尽可能少的数据”的原则,发现读取行数最多的步骤读取了4792行。进而发现这个步骤没有用到索引(NULL)。而这个没有用索引的表是orders的supplier_id列。

    加索引试试看:

    alter table orders add index(supplier_id);

    再次使用explain查看执行计划:

    可以看到这个步骤使用了索引,读取的行数减少到了599行。

    实际执行一下,秒出。

    3.explain执行计划各个字段的意义:

    1)id:语句的执行顺序,倒序执行

    2)select_type:主要有以下几个类型:

    lsimple:表示简单的select,没有union和子查询

    lprimary:最外层的select。在有子查询的语句中,最外面的select查询就是primary

    lunion:union语句的第二个或者说是后面那一个

    lunion result:union的结果

    lsubquery: 子查询中的第一个 select

    3)table:涉及的表。

    4)type:连接类型。主要有以下几个:(重点查看)

    lconst:说明只有一个匹配行,使用了主键或唯一性索引。通常是最优化的情况。

    leq_ref,ref,ref_or_null:表示走了简单索引

    lindex_merge:表示使用了多个索引的组合

    lrange:表示通过索引取出了一个范围内的值。例如where a in (1,2)

    lindex:表示对索引进行了全扫描

    lALL:表示全表扫描

    注意:以上类型从上到下性能越来越差。

    5)possible_keys:可供使用的索引

    6)keys:实际使用的索引

    7)key_gen:索引长度

    8)ref:显示使用哪个列或常数与索引一起从表中选择行

    9)rows:读取的行数。(重点查看)

    10)Extra:备注

    相关文章

      网友评论

          本文标题:Mysql语句优化的原则——让你写sql更加顺手

          本文链接:https://www.haomeiwen.com/subject/buhtgxtx.html