码迷,mamicode.com
首页 > 其他好文 > 详细

优化案例1-尽量避免使用自定义函数进行大量运算

时间:2017-07-28 15:46:00      阅读:201      评论:0      收藏:0      [点我收藏+]

标签:说明   net   access   连接   函数   代码优化   方式   执行   row   

案例说明
在月底进行代码优化检查过程中。在SQL检查过程之执行次数最多的SQL。发现SQL_ID为grk7dk5amf5m7和gzzzkzbfg8j2m 在半个小时内产生大约分别15亿次执行。逻辑读也有15G
其实SQL本身很简单;是一个自定义的分割函数。

技术分享

原SQL

  select to_char(a.logintime, yyyymmdd),
           to_char(a.logintime, HH24),
           2552,
           substr(a.qn, 5, 4) ad,
           regexp_substr(a.qn, [^_]+, 1, 2) channelid,
           a.account,
           sysdate
      from ssdk_acc_login a
     where to_char(a.logintime, yyyymmdd) = 20170728
       and splitstr(a.qn, 2, _) in
           (select cid from tbl_channel where channel_tag like 广点通%);

该SQL的执行计划

Execution Plan
----------------------------------------------------------
Plan hash value: 2543353618

-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                    |  7236 |   558K|  2445   (1)| 00:00:30 |
|   1 |  VIEW                          | VM_NWVW_2          |  7236 |   558K|  2445   (1)| 00:00:30 |
|   2 |   HASH UNIQUE                  |                    |  7236 |   607K|  2445   (1)| 00:00:30 |
|*  3 |    HASH JOIN                   |                    |  7236 |   607K|  2444   (1)| 00:00:30 |
|*  4 |     TABLE ACCESS FULL          | TBL_CHANNEL        |   154 |  2772 |     5   (0)| 00:00:01 |
|   5 |     TABLE ACCESS BY INDEX ROWID| SSDK_ACC_LOGIN     | 95232 |  6324K|  2438   (1)| 00:00:30 |
|*  6 |      INDEX RANGE SCAN          | IND_SDK_LOGIN_TIME | 95232 |       |   670   (1)| 00:00:09 |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("CID"="SPLITSTR"(INTERNAL_FUNCTION("A"."QN"),2,_))
   4 - filter("CHANNEL_TAG" LIKE 广点通%)
   6 - access(TO_CHAR(INTERNAL_FUNCTION("LOGINTIME"),yyyymmdd)=20170728)


Statistics
----------------------------------------------------------
     152770  recursive calls
    1833240  db block gets
     307000  consistent gets
          0  physical reads
          0  redo size
    1465885  bytes sent via SQL*Net to client
      20225  bytes received via SQL*Net from client
       1793  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      26867  rows processed    

从执行计划上来看。存在有152770递归调用和约200M的逻辑读。但是返回的行数只有2万条。逻辑读跟返回的行数严重失调。本身表ssdk_acc_login是一个大表。
对有经验的人来说;产生大量的递归条用;很容易看出问题跟自定义函数SPLITSTR有关。

通过等价改下SQL

 

    select to_char(a.logintime, yyyymmdd),
           to_char(a.logintime, HH24),
           2552,
           substr(a.qn, 5, 4) ad,
           regexp_substr(a.qn, [^_]+, 1, 2) channelid,
           a.account,
           sysdate
      from ssdk_acc_login a
     where to_char(a.logintime, yyyymmdd) = 20170728
       and regexp_substr(a.qn,[^_]+,1,2) in
           (select cid from tbl_channel where channel_tag like 广点通%);

 

该SQL 的执行计划

 

Execution Plan
----------------------------------------------------------
Plan hash value: 2829062945

---------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                    |    47 |  3478 |  2443   (1)| 00:00:30 |
|   1 |  NESTED LOOPS                |                    |    47 |  3478 |  2443   (1)| 00:00:30 |
|   2 |   SORT UNIQUE                |                    |   154 |  2772 |     5   (0)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL         | TBL_CHANNEL        |   154 |  2772 |     5   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS BY INDEX ROWID| SSDK_ACC_LOGIN     |    47 |  2632 |  2437   (1)| 00:00:30 |
|*  5 |    INDEX RANGE SCAN          | IND_SDK_LOGIN_TIME | 95232 |       |   669   (1)| 00:00:09 |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter("CHANNEL_TAG" LIKE 广点通%)
   4 - filter("CID"= REGEXP_SUBSTR ("A"."QN",[^_]+,1,2))
   5 - access(TO_CHAR(INTERNAL_FUNCTION("LOGINTIME"),yyyymmdd)=20170728)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
     214489  consistent gets
          0  physical reads
        556  redo size
    1345632  bytes sent via SQL*Net to client
      20324  bytes received via SQL*Net from client
       1802  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      27004  rows processed     

 

执行计划
与之前的SQL的执行计划相比;代价相差无几。连接方式由hash连接改为NESTED LOOPS
统计信息:
递归调用:前面是152770次。现在是0次。
逻辑读: 前面是约200M。现在是约20M。相差10倍。
redo日志。前面不产生。现在产生556kb。影响不大。
排序: 前面不产生;现在产生排序。此服务器专用于oracle。内存48G。影响不大。

综上所述:用正则函数regexp_substr来替换 自定义函数splitstr更好。尽量避免使用自定义函数进行大量运算

 

优化案例1-尽量避免使用自定义函数进行大量运算

标签:说明   net   access   连接   函数   代码优化   方式   执行   row   

原文地址:http://www.cnblogs.com/lottu/p/7250271.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!