• 【Oracle】也来记一次慢SQL的优化


    本文创意来自:微信公众号“占小狼的博客”21年9月13日博文 “SQL要快:记一次慢SQL优化”

    下文将用到的表:

    create table transfer(
    id int,
    from_account_id nvarchar2(20),
    to_account_id nvarchar2(20),
    amount int,
    free int,
    status int,
    comments nvarchar2(20),
    create_time timestamp default sysdate,
    update_time timestamp default sysdate,
    primary key(id));

    给表填充两百万测试数据:

    insert into transfer(id,from_account_id,to_account_id,amount,status,comments)
    select rownum,
           dbms_random.string('*',dbms_random.value(6,20)),
           dbms_random.string('*',dbms_random.value(6,20)),
           dbms_random.value(1,91000),
           dbms_random.value(1,12),
           dbms_random.string('*',dbms_random.value(6,20))
    from dual
    connect by level<2000001;

    然后先找点数据出来下面查询要用:

    SQL>  select status,to_account_id from transfer where rownum<10;
    
        STATUS TO_ACCOUNT_ID
    ---------- ----------------------------------------
             7 YWSRBVSVWUVKUNZRFNHE
             7 IQNFNHLHQ
             5 QBXOPTUDODVILMSCC
             3 OQZDUIJ
             6 SZSAGORE
             4 UKFCUVCHULUKEQODLP
             2 YAAFOMVO
            10 UQHSEHGAUTNJBINHB
            10 XRXJMDYP
    
    已选择9行。

    首先把原作者要优化的目标SQL拿出来跑一遍,现在是没有索引(除了主键索引)的情况。

    无index查询
    select id,from_account_id,to_account_id,amount,status,comments,create_time,update_time
    from transfer
    where status=10 and to_account_id='XRXJMDYP' and rownum<1500
    order by amount desc,create_time asc
    
    已用时间:  00: 00: 01.99

    接近两秒,有点长,这还是Oracle数据库,换MySQL会更长。

    现在打算给查询字段status,to_account_id和排序字段amount,create_time加联合索引,因为status的范围是1-12,按上面的筛法,取status=10的数据,那么无论其它条件如何选中的数据必然小于20%(1/12<0.2),那么加上的索引一定会用上的,不会出现所选数据大于两成而导致全表查询的情况发生。

     create index idx_tf_4 on transfer(status,to_account_id,amount,create_time);
     已用时间:  00: 00: 15.36

    加索引花的时间不少。

    再查一次看优化了多少:

    有index查询
    select id,from_account_id,to_account_id,amount,status,comments,create_time,update_time
    from transfer
    where status=7 and to_account_id='YWSRBVSVWUVKUNZRFNHE' and rownum<1500
    order by amount desc,create_time asc
     已用时间:  00: 00: 00.06

    已经是秒出,看来索引确实奏效了。

    让我们再把索引删除。

    drop index idx_tf_4;

    删除这个索引是因为不想让它影响下面的新查询效果。

    先筛选后连接,先筛选后排序是基本的优化SQL思想,按这个思想我们可以把符合的记录找出再排序。

    select id,from_account_id,to_account_id,amount,status,comments,create_time,update_time
    from transfer where id in (select id from transfer where status=10 and to_account_id='XRXJMDYP' and rownum<1500 )
    order by amount desc,create_time asc
    
    已用时间:  00: 00: 00.43

    看,没加索引,只是用了id身上的固有索引,查询结果就是秒出,从此可以看出“先筛选后连接,先筛选后排序”的有效性。

    前文说过Oracle CBO的强大,但CBO毕竟是机器,写出节省时间的SQL还需要人的积极参与。

    现在秒出,再加个索引试试:

    create index idx_tf_2 on transfer(status,to_account_id);
    已用时间:  00: 00: 05.86

    花的时间比四字段索引少三分之二,加完此索引后再查一次:

    select id,from_account_id,to_account_id,amount,status,comments,create_time,update_time
    from transfer where id in (select id from transfer where status=5 and to_account_id='QBXOPTUDODVILMSCC' and rownum<1500 )
    order by amount desc,create_time asc
    已用时间:  00: 00: 00.17

    加索引确实有效果。

    删除此索引把前面的索引加上:

    SQL> drop index idx_tf_2;
    
    索引已删除。
    
    已用时间:  00: 00: 00.06
    SQL> create index idx_tf_4 on transfer(status,to_account_id,amount,create_time);
    
    索引已创建。
    
    已用时间:  00: 00: 09.87

    再看查询效果:

    select id,from_account_id,to_account_id,amount,status,comments,create_time,update_time
    from transfer where id in (select id from transfer where status=6 and to_account_id='SZSAGORE' and rownum<1500 )
    order by amount desc,create_time asc
    已用时间:  00: 00: 00.17

    和加两字段索引差不多,这是因为idx_tf_2里的两字段也出现在了idx_tf_4中。

    但是,加索引对插入更新的效果是负面的,换了SQL写法后已经是秒出,未必有再优化的必要,加了索引也只是在查询上锦上添花,插入更新是有损的,因此就本例来说,换新的先筛选后排序的SQL是最优解。

    -END-

  • 相关阅读:
    java 多线程4: java线程的优先级
    Thread.currentThread().getName() ,对象实例.getName() 和 this.getName()区别
    go http
    go redis
    go tcp
    go 单元测试
    go 定时器
    go channel
    go goroutine
    go 错误处理
  • 原文地址:https://www.cnblogs.com/heyang78/p/15264471.html
Copyright © 2020-2023  润新知