• 记一次SQL性能优化,查询时间从4000ms优化到200ms.


    以下这句SQL是从PLM中获取代办工作流的。没优化前SQL语句执行一次大概4000ms(4秒)。

     1 select ch.change_number changeNumber, f.text changeDetail, u1.last_name creator,
     2 l.value status, to_char(create_date,'yyyy-mm-dd hh24:mi:ss') createDate, 
     3 so.signoff_status type 
     4 from agile.change ch 
     5 inner join agile.workflow_process wp on (ch.id=wp.change_id and ch.status=wp.state and wp.changed_by is null)
     6 inner join agile.signoff so on wp.id=so.process_id and so.signoff_status in (0,4) and so.required in(1,5)
     7 inner join agile.agileuser u on u.id=so.user_assigned 
     8 inner join agile.langtable l on (ch.status=l.id and l.type=4450 and l.langid=4)
     9 inner join agile.agileuser u1 on ch.originator=u1.id
    10 left join agile.agile_flex f on f.id=ch.id and f.attid=2017
    11 where (ch.subclass in (2473549,2473495,1,2475794,2473579,2474897,2473519,2480885,2479577,2473531,2485198,2479783) 
    12 or (ch.subclass = 2478248 and l.value = '审批'))
    13 and u.email ='zhangsan@kedacom.com'

    使用autotrace分析sql

    1 set autotrace on
    2 set timing on

    分析结果如下:

    Elapsed: 00:00:00.008
    Plan hash value: 1883359798
     
    ------------------------------------------------------------------------------------------------------
    | Id  | Operation                         | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                  |                  |    24 |  5280 |   954   (1)| 00:00:12 |
    |   1 |  NESTED LOOPS OUTER               |                  |    24 |  5280 |   954   (1)| 00:00:12 |
    |   2 |   NESTED LOOPS                    |                  |    22 |  3564 |   921   (1)| 00:00:12 |
    |   3 |    NESTED LOOPS                   |                  |    22 |  3234 |   910   (1)| 00:00:11 |
    |   4 |     NESTED LOOPS                  |                  |    56 |  6328 |   882   (1)| 00:00:11 |
    |   5 |      NESTED LOOPS                 |                  |   156 | 11076 |   804   (1)| 00:00:10 |
    |   6 |       NESTED LOOPS                |                  |   212 | 10176 |   592   (1)| 00:00:08 |
    |*  7 |        TABLE ACCESS FULL          | AGILEUSER        |     1 |    29 |   168   (1)| 00:00:03 |
    |*  8 |        TABLE ACCESS BY INDEX ROWID| SIGNOFF          |   210 |  3990 |   424   (0)| 00:00:06 |
    |*  9 |         INDEX RANGE SCAN          | SIGNOFF_IDX4     |  1120 |       |     3   (0)| 00:00:01 |
    |* 10 |       TABLE ACCESS BY INDEX ROWID | WORKFLOW_PROCESS |     1 |    23 |     1   (0)| 00:00:01 |
    |* 11 |        INDEX UNIQUE SCAN          | WF_PROCESS_PK    |     1 |       |     1   (0)| 00:00:01 |
    |* 12 |      TABLE ACCESS BY INDEX ROWID  | CHANGE           |     1 |    42 |     1   (0)| 00:00:01 |
    |* 13 |       INDEX UNIQUE SCAN           | CHANGE_PK        |     1 |       |     1   (0)| 00:00:01 |
    |* 14 |     TABLE ACCESS BY INDEX ROWID   | LANGTABLE        |     1 |    34 |     1   (0)| 00:00:01 |
    |* 15 |      INDEX UNIQUE SCAN            | LANGTABLE_PK     |     1 |       |     1   (0)| 00:00:01 |
    |  16 |    TABLE ACCESS BY INDEX ROWID    | AGILEUSER        |     1 |    15 |     1   (0)| 00:00:01 |
    |* 17 |     INDEX UNIQUE SCAN             | AGILEUSER_PK     |     1 |       |     1   (0)| 00:00:01 |
    |  18 |   TABLE ACCESS BY INDEX ROWID     | AGILE_FLEX       |     1 |    58 |     2   (0)| 00:00:01 |
    |* 19 |    INDEX RANGE SCAN               | AGILE_FLEX_UQ    |     1 |       |     1   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
     
       7 - filter("U"."EMAIL"='fanjiangen@kedacom.com')
       8 - filter(("SO"."REQUIRED"=1 OR "SO"."REQUIRED"=5) AND ("SO"."SIGNOFF_STATUS"=0 OR 
                  "SO"."SIGNOFF_STATUS"=4))
       9 - access("U"."ID"="SO"."USER_ASSIGNED")
      10 - filter("WP"."CHANGED_BY" IS NULL)
      11 - access("WP"."ID"="SO"."PROCESS_ID")
      12 - filter(("CH"."SUBCLASS"=1 OR "CH"."SUBCLASS"=2473495 OR "CH"."SUBCLASS"=2473519 OR 
                  "CH"."SUBCLASS"=2473531 OR "CH"."SUBCLASS"=2473549 OR "CH"."SUBCLASS"=2473579 OR 
                  "CH"."SUBCLASS"=2474897 OR "CH"."SUBCLASS"=2475794 OR "CH"."SUBCLASS"=2478248 OR 
                  "CH"."SUBCLASS"=2479577 OR "CH"."SUBCLASS"=2479783 OR "CH"."SUBCLASS"=2480885 OR 
                  "CH"."SUBCLASS"=2485198) AND "CH"."STATUS"="WP"."STATE")
      13 - access("CH"."ID"="WP"."CHANGE_ID")
      14 - filter("CH"."SUBCLASS"=1 OR "CH"."SUBCLASS"=2473495 OR "CH"."SUBCLASS"=2473519 OR 
                  "CH"."SUBCLASS"=2473531 OR "CH"."SUBCLASS"=2473549 OR "CH"."SUBCLASS"=2473579 OR 
                  "CH"."SUBCLASS"=2474897 OR "CH"."SUBCLASS"=2475794 OR "CH"."SUBCLASS"=2479577 OR 
                  "CH"."SUBCLASS"=2479783 OR "CH"."SUBCLASS"=2480885 OR "CH"."SUBCLASS"=2485198 OR 
                  "CH"."SUBCLASS"=2478248 AND "L"."VALUE"='审批')
      15 - access("L"."LANGID"=4 AND "L"."TYPE"=4450 AND "CH"."STATUS"="L"."ID")
      17 - access("CH"."ORIGINATOR"="U1"."ID")
      19 - access("F"."ID"(+)="CH"."ID" AND "F"."ATTID"(+)=2017)
           filter("F"."ATTID"(+)=2017)
    
       Statistics
    -----------------------------------------------------------
                   1  DB time
                   1  Requests to/from client
                   2  non-idle wait count
                   1  opened cursors cumulative
                   1  opened cursors current
                   1  pinned cursors current
               58008  session uga memory
                   2  user calls

    从解释计划中可以看出有2个地方预估时间很长,一个是对agileuser用户表,另一个是signoff,用户审批表。

    用户表总共也就几千条记录,而且邮箱还是唯一的,所以资源耗费不是很大。而signoff表数据基数相当庞大,达到百万级。

    虽然已经走了索引了,但是走的索引是审批用户的ID这个字段。对于绝大部分人来说,其下的数据不是很多。所以查询起来不会很慢。

    但对于某些重要领导(大部分审批流都要他批的那种)数据量就会变得相当庞大。而这个SQL执行慢也恰好是遇到了这样的领导才会慢。

    经过具体问题,根据某领导张三的用户ID可以在这张表中查询到2万多数据。也就是说数据库引擎根据现有索引找到该用户的记录后,还要从2万多记录中找到当前需要审批的记录。

    分析至此,考虑将用户id以及sql语句中另外的2个条件加入组合索引。重新使用autotrace分析语句,结果如下:

    Elapsed: 00:00:00.008
    Plan hash value: 441713506
     
    -------------------------------------------------------------------------------------------------------
    | Id  | Operation                          | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                   |                  |    24 |  5280 |   622   (1)| 00:00:08 |
    |   1 |  NESTED LOOPS OUTER                |                  |    24 |  5280 |   622   (1)| 00:00:08 |
    |   2 |   NESTED LOOPS                     |                  |    22 |  3564 |   589   (1)| 00:00:08 |
    |   3 |    NESTED LOOPS                    |                  |    22 |  3234 |   578   (1)| 00:00:07 |
    |   4 |     NESTED LOOPS                   |                  |    56 |  6328 |   550   (1)| 00:00:07 |
    |   5 |      NESTED LOOPS                  |                  |   156 | 11076 |   472   (1)| 00:00:06 |
    |   6 |       NESTED LOOPS                 |                  |   212 | 10176 |   260   (1)| 00:00:04 |
    |*  7 |        TABLE ACCESS FULL           | AGILEUSER        |     1 |    29 |   168   (1)| 00:00:03 |
    |   8 |        INLIST ITERATOR             |                  |       |       |            |          |
    |   9 |         TABLE ACCESS BY INDEX ROWID| SIGNOFF          |   210 |  3990 |    93   (2)| 00:00:02 |
    |* 10 |          INDEX RANGE SCAN          | SIGNOFF_IDX_KD_1 |   210 |       |     3  (34)| 00:00:01 |
    |* 11 |       TABLE ACCESS BY INDEX ROWID  | WORKFLOW_PROCESS |     1 |    23 |     1   (0)| 00:00:01 |
    |* 12 |        INDEX UNIQUE SCAN           | WF_PROCESS_PK    |     1 |       |     1   (0)| 00:00:01 |
    |* 13 |      TABLE ACCESS BY INDEX ROWID   | CHANGE           |     1 |    42 |     1   (0)| 00:00:01 |
    |* 14 |       INDEX UNIQUE SCAN            | CHANGE_PK        |     1 |       |     1   (0)| 00:00:01 |
    |* 15 |     TABLE ACCESS BY INDEX ROWID    | LANGTABLE        |     1 |    34 |     1   (0)| 00:00:01 |
    |* 16 |      INDEX UNIQUE SCAN             | LANGTABLE_PK     |     1 |       |     1   (0)| 00:00:01 |
    |  17 |    TABLE ACCESS BY INDEX ROWID     | AGILEUSER        |     1 |    15 |     1   (0)| 00:00:01 |
    |* 18 |     INDEX UNIQUE SCAN              | AGILEUSER_PK     |     1 |       |     1   (0)| 00:00:01 |
    |  19 |   TABLE ACCESS BY INDEX ROWID      | AGILE_FLEX       |     1 |    58 |     2   (0)| 00:00:01 |
    |* 20 |    INDEX RANGE SCAN                | AGILE_FLEX_UQ    |     1 |       |     1   (0)| 00:00:01 |
    -------------------------------------------------------------------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
     
       7 - filter("U"."EMAIL"='fanjiangen@kedacom.com')
      10 - access("U"."ID"="SO"."USER_ASSIGNED" AND ("SO"."SIGNOFF_STATUS"=0 OR 
                  "SO"."SIGNOFF_STATUS"=4) AND ("SO"."REQUIRED"=1 OR "SO"."REQUIRED"=5))
      11 - filter("WP"."CHANGED_BY" IS NULL)
      12 - access("WP"."ID"="SO"."PROCESS_ID")
      13 - filter(("CH"."SUBCLASS"=1 OR "CH"."SUBCLASS"=2473495 OR "CH"."SUBCLASS"=2473519 OR 
                  "CH"."SUBCLASS"=2473531 OR "CH"."SUBCLASS"=2473549 OR "CH"."SUBCLASS"=2473579 OR 
                  "CH"."SUBCLASS"=2474897 OR "CH"."SUBCLASS"=2475794 OR "CH"."SUBCLASS"=2478248 OR 
                  "CH"."SUBCLASS"=2479577 OR "CH"."SUBCLASS"=2479783 OR "CH"."SUBCLASS"=2480885 OR 
                  "CH"."SUBCLASS"=2485198) AND "CH"."STATUS"="WP"."STATE")
      14 - access("CH"."ID"="WP"."CHANGE_ID")
      15 - filter("CH"."SUBCLASS"=1 OR "CH"."SUBCLASS"=2473495 OR "CH"."SUBCLASS"=2473519 OR 
                  "CH"."SUBCLASS"=2473531 OR "CH"."SUBCLASS"=2473549 OR "CH"."SUBCLASS"=2473579 OR 
                  "CH"."SUBCLASS"=2474897 OR "CH"."SUBCLASS"=2475794 OR "CH"."SUBCLASS"=2479577 OR 
                  "CH"."SUBCLASS"=2479783 OR "CH"."SUBCLASS"=2480885 OR "CH"."SUBCLASS"=2485198 OR 
                  "CH"."SUBCLASS"=2478248 AND "L"."VALUE"='审批')
      16 - access("L"."LANGID"=4 AND "L"."TYPE"=4450 AND "CH"."STATUS"="L"."ID")
      18 - access("CH"."ORIGINATOR"="U1"."ID")
      20 - access("F"."ID"(+)="CH"."ID" AND "F"."ATTID"(+)=2017)
           filter("F"."ATTID"(+)=2017)
    
       Statistics
    -----------------------------------------------------------
                   1  CPU used by this session
                   1  Requests to/from client
                   2  non-idle wait count
                   1  opened cursors cumulative
                   1  opened cursors current
                   1  pinned cursors current
                   2  user calls
    >>Query Run In:查询结果 3

    从上面的结果可以看出,sql走入了新建的组合索引(SIGNOFF_IDX_KD_1),预估执行时间从原来的06秒变成02秒,cost也大幅度下降,整句SQL的执行时间也从4秒多下降到了200ms。

    由此可见,索引对于查询的优化真是显而易见。

  • 相关阅读:
    Alignment
    Matrix 二维树状数组的第二类应用
    网络请求中的URL中传bool型数据
    把推送证书给服务器
    完全取代VC上原有的view
    图层CALayer的使用
    数组使用的注意事项
    使用CocoaPods
    声明遵循协议
    神奇的navigationBar.translucent
  • 原文地址:https://www.cnblogs.com/namelessmyth/p/9251690.html
Copyright © 2020-2023  润新知